百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

SpringCloud源码之API网关Gateway

suiw9 2024-10-28 18:33 40 浏览 0 评论


一、概念:

在微服务架构下,每个微服务都有不同的IP地址和端口,前端在访问时就需要针对每个微服务单独访问,一旦新增或者调整微服务后,前端也需要做同步的修改,非常麻烦,而且为了校验访问请求是合法的,每个微服务都需要都需要做安全校验。引入API网关后,前端就只需要访问API网关就行了,里面的请求转发和一些其他的验权都可以放在API网关里面进行统一处理。因此API网关的职责一是路由功能,根据定义的请求规则和转化的规则,将传入的请求映射到不同的微服务进行处理,另一个职责就是针对请求信息在API网关进行统一验权。SpringCloud Gateway是一款非常优秀性能强劲的API网关。

SpringCloud Gateway接收请求的总入口是DispatcherHandler,有两个非常重要的核心概念,就是路由(Route)和过滤器(Filter)。

1、路由(Route):Route 是网关的基础元素,由 ID、目标 URI、断言、过滤器组成。当请求到达网关时,由 Gateway HandlerMapping 通过断言进行路由匹配(Mapping),当断言为真时,匹配到路由。

Route的定义如下:

arduino

复制代码

publicclassRoute implements Ordered {privatefinalString id;privatefinal URI uri;privatefinalint order;privatefinal AsyncPredicate<ServerWebExchange> predicate;privatefinal List<GatewayFilter> gatewayFilters;privatefinal Map<String, Object> metadata; ...........}

在配置文件定义的Route信息在SpringCloud Gateway里面叫做RouteDefinition,其定义如下:

typescript

复制代码

publicclassRouteDefinition {privateString id;privateList<PredicateDefinition> predicates = newArrayList<>();privateList<FilterDefinition> filters = newArrayList<>();privateURI uri;privateMap<String, Object> metadata = newHashMap<>();private int order = 0; ...........}publicclassPredicateDefinition {privateString name;privateMap<String, String> args = newLinkedHashMap<>(); ............}publicclassFilterDefinition {privateString name;privateMap<String, String> args = newLinkedHashMap<>(); ................}

2、过滤器:过滤器可以在请求到达业务代码之前执行一些业务上的处理。SpringCloud gateway中有两种过滤器,路由过滤器(接口为GatewayFilter)和全局过滤器(接口为GlobalFilter);

路由过滤器GatewayFilter的定义如下:

kotlin

复制代码

publicinterfaceGatewayFilter {/** * Process the Web request and (optionally) delegate to the next * {@code WebFilter} through the given {@link GatewayFilterChain}. * @param exchange the current server exchange * @param chain provides a way to delegate to the next filter * @return {@code Mono<Void>} to indicate when request processing is complete */ Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);}

全局过滤器GlobalFilter的定义如下:

kotlin

复制代码

publicinterfaceGlobalFilter {/** * Process the Web request and (optionally) delegate to the next * {@code WebFilter} through the given {@link GatewayFilterChain}. * @param exchange the current server exchange * @param chain provides a way to delegate to the next filter * @return {@code Mono<Void>} to indicate when request processing is complete */ Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);}

二、使用Demo:

1、引入依赖:

xml

复制代码

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency>//由于采用了基于注册中心的动态路由,而注册中心使用的是eureka,因此需要引入eureka的包<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>

2、配置文件application.yml:

yaml

复制代码

spring:cloud:gateway:discovery:locator:enabled:truelowerCaseServiceId:trueroutes:#netty 路由过滤器,http或https开头-id:app1-routeuri:http://www.baidu.compredicates:-Path=/app1/**filters:#转发请求时去掉1级前缀-StripPrefix=1

3、实现Filter:

kotlin

复制代码

@ConfigurationpublicclassAccessFilterimplementsGlobalFilter, Ordered {@Overridepublic int getOrder() {return Ordered.HIGHEST_PRECEDENCE;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //鉴权 Mono<Void> check = checkAccess(exchange);return check != null ? check : chain.filter(exchange);}

三、总流程:

首先来看一张官网上的SpringCloud Gateway的架构图:

1、客户端发送请求到Spring Cloud Gateway,也就是DispatcherHandler接收了请求,如果Gateway Handler Mapping 判定 这个请求和路由是匹配的,则这个请求会被发送到Gateway Web Handler。
2、Gateway Web Handler通过与该请求匹配的过滤器链执行该请求。

再具体点的流程图:

SpringCloud Gateway API网关的流程:
1、加载路由定义RouteDefinition的集合;
2、将RouteDefinition集合转换为Route实例集合;
3、DispatcherHandler根据URL请求匹配Route;
4、交由WebHandler(FilteringWebHandler)执行过滤器链;
5、执行业务代码,并处理返回结果数据;

下面将分别针对这个流程步骤进行解析。

四、加载路由定义RouteDefinition的集合:

RouteDefinition加载的接口是RouteDefinitionLocator,这个接口有多个实现,最常见的RouteDefinition分别是application.yml定义和从配置中心加载。

RouteDefinition接口的定义只有只有一个方法getRouteDefinitions,代码如下:

csharp

复制代码

publicinterfaceRouteDefinitionLocator {Flux<RouteDefinition> getRouteDefinitions();}

1、application.yml定义的路由信息加载RouteDefinition:

比如上面的demo,就是在配置文件application.yml中定义路由信息的,其加载是由PropertiesRouteDefinitionLocator完成的。代码如下:

kotlin

复制代码

publicclassPropertiesRouteDefinitionLocatorimplementsRouteDefinitionLocator {privatefinal GatewayProperties properties;public PropertiesRouteDefinitionLocator(GatewayProperties properties) {this.properties = properties; }@Overridepublic Flux<RouteDefinition> getRouteDefinitions() {return Flux.fromIterable(this.properties.getRoutes()); }}@ConfigurationProperties("spring.cloud.gateway")publicclassGatewayProperties { private List<RouteDefinition> routes = new ArrayList<>(); ................}

由GatewayProperties类可以看到SpringBoot启动时会将application.yml中定义的spring.cloud.gateway.routes下面的所有的信息都加载到routes中,PropertiesRouteDefinitionLocator直接引用GatewayProperties就可以读取到application.yml中定义的RouteDefinition。

2、从配置中心加载RouteDefinition:

从配置中心加载RouteDefinition是由DiscoveryClientRouteDefinitionLocator完成的。其定义和加载代码如下:

ini

复制代码

public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator { //管理以spring.cloud.gateway.discovery.locator开头的属性定义 private final DiscoveryLocatorProperties properties; private final String routeIdPrefix; //存储注册中心的所有服务实例 private Flux<List<ServiceInstance>> serviceInstances; public DiscoveryClientRouteDefinitionLocator(ReactiveDiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) { this(discoveryClient.getClass().getSimpleName(), properties); //通过discoveryClient.getServices和discoveryClient.getInstances获取配置中心中的所有服务实例serviceInstances = discoveryClient.getServices().flatMap(service -> discoveryClient.getInstances(service).collectList()); } public Flux<RouteDefinition> getRouteDefinitions() { .........省略部分代码............ return serviceInstances.filter(instances -> !instances.isEmpty()) .map(instance -> { //从注册中心的服务实例信息构造RouteDefinition RouteDefinition routeDefinition = buildRouteDefinition(urlExpr, instance); //构造RouteDefinition的断言信息 for (PredicateDefinition original : this.properties.getPredicates()) { PredicateDefinition predicate = new PredicateDefinition(); predicate.setName(original.getName()); for (Map.Entry<String, String> entry : original.getArgs().entrySet()) { String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry); predicate.addArg(entry.getKey(), value); } routeDefinition.getPredicates().add(predicate); } //构造RouteDefinition的过滤器链信息 for (FilterDefinition original : this.properties.getFilters()) { FilterDefinition filter = new FilterDefinition(); filter.setName(original.getName()); for (Map.Entry<String, String> entry : original.getArgs() .entrySet()) { String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry); filter.addArg(entry.getKey(), value); } routeDefinition.getFilters().add(filter); } return routeDefinition; }); } protected RouteDefinition buildRouteDefinition(Expression urlExpr, ServiceInstance serviceInstance) { String serviceId = serviceInstance.getServiceId(); RouteDefinition routeDefinition = new RouteDefinition(); routeDefinition.setId(this.routeIdPrefix + serviceId); String uri = urlExpr.getValue(this.evalCtxt, serviceInstance, String.class); routeDefinition.setUri(URI.create(uri)); // add instance metadata routeDefinition.setMetadata(new LinkedHashMap<>(serviceInstance.getMetadata())); return routeDefinition; }}@ConfigurationProperties("spring.cloud.gateway.discovery.locator")public class DiscoveryLocatorProperties { private boolean enabled = false; private String routeIdPrefix; private String includeExpression = "true"; private String urlExpression = "'lb://'+serviceId"; private boolean lowerCaseServiceId = false; private List<PredicateDefinition> predicates = new ArrayList<>(); private List<FilterDefinition> filters = new ArrayList<>(); ................}

1、DiscoveryClientRouteDefinitionLocator的构造函数中,会调用discoveryClient.getServices和discoveryClient.getInstances获取配置中心中的所有服务实例信息保存到serviceInstances中;

2、遍历serviceInstances,buildRouteDefinition依据每一个ServiceInstance构造一个RouteDefinition对象,predicates和filters信息则从DiscoveryLocatorProperties中读取,而DiscoveryLocatorProperties对于predicates和filters集合则只有定义而没有赋值,是不是很好奇他是怎么生成的,看看GatewayDiscoveryClientAutoConfiguration的中的配置就恍然大悟了:

less

复制代码

@Configuration(proxyBeanMethods = false)@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)@AutoConfigureBefore(GatewayAutoConfiguration.class)@AutoConfigureAfter(CompositeDiscoveryClientAutoConfiguration.class)@ConditionalOnClass({ DispatcherHandler.class })@EnableConfigurationPropertiespublic class GatewayDiscoveryClientAutoConfiguration { ...........省略部分代码......... @BeanpublicDiscoveryLocatorPropertiesdiscoveryLocatorProperties() {DiscoveryLocatorPropertiesproperties = newDiscoveryLocatorProperties();properties.setPredicates(initPredicates());properties.setFilters(initFilters());returnproperties; }publicstaticList<PredicateDefinition> initPredicates() {ArrayList<PredicateDefinition> definitions = newArrayList<>(); PredicateDefinitionpredicate = newPredicateDefinition();predicate.setName(normalizeRoutePredicateName(PathRoutePredicateFactory.class));predicate.addArg("pattern", "'/'+serviceId+'/**'");definitions.add(predicate);returndefinitions; }publicstaticList<FilterDefinition> initFilters() {ArrayList<FilterDefinition> definitions = newArrayList<>();FilterDefinitionfilter = newFilterDefinition();filter.setName(normalizeFilterFactoryName(RewritePathGatewayFilterFactory.class));Stringregex = "'/' + serviceId + '/(?<remaining>.*)'";Stringreplacement = "'/${remaining}'";filter.addArg("regexp", regex);filter.addArg("replacement", replacement);definitions.add(filter);returndefinitions; }}

五、将RouteDefinition集合转换为Route实例集合:

RouteLocator接口用于将RouteDefinition转换为Route实例,这个接口只定义了一个方法getRoutes,代码如下:

csharp

复制代码

publicinterfaceRouteLocator {Flux<Route> getRoutes();}

这个接口有三个实现类:CachingRouteLocator,CompositeRouteLocator,RouteDefinitionRouteLocator,这里以RouteDefinitionRouteLocator为例进行分析,代码如下:

kotlin

复制代码

publicclassRouteDefinitionRouteLocatorimplementsRouteLocator, BeanFactoryAware, ApplicationEventPublisherAware { ..........省略部分代码..........privatefinal RouteDefinitionLocator routeDefinitionLocator;privatefinal ConfigurationService configurationService;privatefinal Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();privatefinal Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();privatefinal GatewayProperties gatewayProperties;public Flux<Route> getRoutes() {//将RouteDefinition集合转换为Route实例集合 Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute);return routes.map(route -> { return route; }); }}

getRoutes中this.routeDefinitionLocator.getRouteDefinitions获取所有的RouteDefinition,这个在上面已经分析过了,接下来分析下convertToRoute是如何将RouteDefinition转换为Route实例的:

scss

复制代码

private Route convertToRoute(RouteDefinition routeDefinition) { AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition); List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);//使用构造器AsyncBuilder构造Route实例 return Route.async(routeDefinition).asyncPredicate(predicate).replaceFilters(gatewayFilters).build();}public static AsyncBuilder async(RouteDefinition routeDefinition) { return new AsyncBuilder().id(routeDefinition.getId()).uri(routeDefinition.getUri()).order(routeDefinition.getOrder()).metadata(routeDefinition.getMetadata()); }public Route build() { return new Route(this.id, this.uri, this.order, predicate, this.gatewayFilters, this.metadata);}

这里使用了构造器AsyncBuilder来构造Route实例,Route.async首先从routeDefinition读取信息到构造器,然后设置构造器的Predicate和Filter,最后构造Route实例。

六、根据Http请求匹配Route:

1、流程:

SpringCloud Gateway依赖于框架Spring WebFlux,因此他的总入口不是DispatcherServlet而是DispatcherHandler,入口方法在handle中:

scss

复制代码

public Mono<Void> handle(ServerWebExchange exchange) { return Flux.fromIterable(this.handlerMappings)//匹配到Route.concatMap(mapping -> mapping.getHandler(exchange)).next().switchIfEmpty(createNotFoundError())//通过FilteringWebHandler执行过滤器链和执行业务代码.flatMap(handler -> invokeHandler(exchange, handler))//处理返回结果数据.flatMap(result -> handleResult(exchange, result));}

a、首先使用HandlerMapping从http请求匹配到Route,并返回FilteringWebHandler对象(mapping.getHandler(exchange));

b、接着通过FilteringWebHandler执行过滤器链和执行业务代码(invokeHandler(exchange, handler));

c、处理返回结果数据(handleResult(exchange, result));

2、HandlerMapping的初始化:

在DispatcherHandler的构造方法中从Spring容器过滤出所有实现了HandlerMapping接口的bean。

ini

复制代码

public class DispatcherHandler implements WebHandler, ApplicationContextAware { private List<HandlerMapping> handlerMappings; private List<HandlerAdapter> handlerAdapters; private List<HandlerResultHandler> resultHandlers; public DispatcherHandler(ApplicationContext applicationContext) { initStrategies(applicationContext); } protected void initStrategies(ApplicationContext context) { Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);this.handlerMappings = Collections.unmodifiableList(mappings); Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);this.handlerAdapters = new ArrayList<>(adapterBeans.values()); Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerResultHandler.class, true, false);this.resultHandlers = new ArrayList<>(beans.values()); } ................}

3、HandlerMapping匹配Route

HandlerMapping接口用于从http请求信息匹配到对应的Route,这个接只定义了一个方法getHandler,代码如下:

csharp

复制代码

publicinterfaceHandlerMapping {Mono<Object> getHandler(ServerWebExchange exchange);}

HandlerMapping接口的实现类有很多,我们这里就分析下RoutePredicateHandlerMapping实现类是怎么匹配的流程:

scss

复制代码

public Mono<Object> getHandler(ServerWebExchange exchange) { return getHandlerInternal(exchange).map(handler -> { return handler; });}protected Mono<?> getHandlerInternal(ServerWebExchange exchange) { //lookupRoute依据exchange匹配Route return lookupRoute(exchange) .flatMap((Function<Route, Mono<?>>) r -> { exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);//返回FilteringWebHandler对象 return Mono.just(webHandler); });}protected Mono<Route> lookupRoute(ServerWebExchange exchange) {//routeLocator.getRoutes()返回所有的Route实例 return this.routeLocator.getRoutes() .concatMap(route -> Mono.just(route).filterWhen(r -> { exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());//过滤出断言为true的route return r.getPredicate().apply(exchange); })); }

this.routeLocator.getRoutes返回所有的Route实例,这在上面已经分析过了。接下来遍历所有的Route,r.getPredicate().apply(exchange)对Route的断言进行判断,过滤出返回true的Route,至此匹配Route路由就完成。

getHandlerInternal最后返回的webHandler对象,从定义可以看到这个 webHandler是FilteringWebHandler类型的,也就是后面将交由他来执行过滤器链。

七、执行过滤器链和业务代码:

1、适配器HandlerAdapter:

由于HandlerMapping不止RoutePredicateHandlerMapping一种实现,还有其他实现,在RoutePredicateHandlerMapping实现中返回的是FilteringWebHandler(带过滤器链的webHandler),而其他类型中有的是返回WebSocketHandler,有的返回的是HandlerMethod(不带过滤器链的,直接执行业务代码)等等,要以一种统一的方式调用的话,就需要封装一个适配器接口HandlerAdapter:

java

复制代码

publicinterfaceHandlerAdapter {booleansupports(Object handler); Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler);}

supports用于判断持哪种类型的Handler,handle方法用于具体handler的调用。

执行业务代码的invokeHandler就是遍历所有的适配器HandlerAdapter,判断哪种适配器支持WebHandler则进行调用:

kotlin

复制代码

private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {if (this.handlerAdapters != null) {for (HandlerAdapter handlerAdapter : this.handlerAdapters) {if (handlerAdapter.supports(handler)) {return handlerAdapter.handle(exchange, handler); } } }return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));}

支持WebHandler的适配器是SimpleHandlerAdapter:

typescript

复制代码

publicclassSimpleHandlerAdapterimplementsHandlerAdapter {@Overridepublicbooleansupports(Object handler) {returnWebHandler.class.isAssignableFrom(handler.getClass()); }@OverridepublicMono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {WebHandler webHandler = (WebHandler) handler;//这里会进入FilteringWebHandler.handle的调用Mono<Void> mono = webHandler.handle(exchange);return mono.then(Mono.empty()); }}

2、FilteringWebHandler的调用:

FilteringWebHandler首先加载全局过滤器:

java

复制代码

publicclassFilteringWebHandlerimplementsWebHandler {privatefinal List<GatewayFilter> globalFilters;publicFilteringWebHandler(List<GlobalFilter> globalFilters) {this.globalFilters = loadFilters(globalFilters); }privatestatic List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {return filters.stream().map(filter -> {//利用适配器将GlobalFilter和GatewayFilter放到一个列表中GatewayFilterAdaptergatewayFilter=newGatewayFilterAdapter(filter); return gatewayFilter; }).collect(Collectors.toList()); } ..............}privatestaticclassGatewayFilterAdapterimplementsGatewayFilter {privatefinal GlobalFilter delegate; GatewayFilterAdapter(GlobalFilter delegate) {this.delegate = delegate; }@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {returnthis.delegate.filter(exchange, chain); }}

全局过滤器GlobalFilter和Route过滤器GatewayFilter不是一个接口,不能放到一个集合中进行统一调用,因此定义了一个适配器GatewayFilterAdapter继承自GatewayFilter,这样两种过滤器就可以放到一个集合中统一调用了。

FilteringWebHandler的调用:

ini

复制代码

public Mono<Void> handle(ServerWebExchange exchange) { Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR); List<GatewayFilter> gatewayFilters = route.getFilters(); //合并全局过滤器和Route过滤器 List<GatewayFilter> combined = new ArrayList<>(this.globalFilters); combined.addAll(gatewayFilters); //交由DefaultGatewayFilterChain执行 return new DefaultGatewayFilterChain(combined).filter(exchange);}

首先将全局过滤器和Route过滤器合并到一个集合,然后交由DefaultGatewayFilterChain执行:

kotlin

复制代码

private static classDefaultGatewayFilterChainimplementsGatewayFilterChain {//当前执行的过滤器的游标privatefinal int index;//过滤器集合privatefinal List<GatewayFilter> filters; DefaultGatewayFilterChain(List<GatewayFilter> filters) {this.filters = filters;this.index = 0; }private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) {this.filters = parent.getFilters();this.index = index; }public List<GatewayFilter> getFilters() {return filters; }@Overridepublic Mono<Void> filter(ServerWebExchange exchange) {return Mono.defer(() -> {if (this.index < filters.size()) { GatewayFilter filter = filters.get(this.index); DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1);//执行过滤器代码return filter.filter(exchange, chain); }else {return Mono.empty(); // complete } }); }}

DefaultGatewayFilterChain是一个过滤器链,里面保存了一个过滤器集合和当前执行的游标,每次执行filter时,根据index取出过滤器执行,然后游标加一,将加一后的游标重新构造DefaultGatewayFilterChain去执行,直至所有的过滤器执行完成,最后返回Mono.empty()表示完成。

不对,所有过滤器执行完后不是应该再去执行业务代码吗,怎么返回Mono.empty()了,这样业务代码是不是就没地方执行了。断点调试看看过滤器链的数据,其中有过滤器NettyRoutingFilter和NettyWriteResponseFilter,NettyRoutingFilter负责发送请求到route目标地址服务执行,NettyWriteResponseFilter则负责将目标服务执行的结果输出到客户端。下面分别看看这两个过滤器。

3、请求业务代码和将执行结果写回客户端:

先看看NettyRoutingFilter的代码:

java

复制代码

publicclassNettyRoutingFilterimplementsGlobalFilter, Ordered {privatefinal HttpClient httpClient; privatefinal HttpClientProperties properties;@OverridepublicintgetOrder() {//排序在过滤器链集合最后return Ordered.LOWEST_PRECEDENCE; }@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {URIrequestUrl= exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);Stringscheme= requestUrl.getScheme();if (isAlreadyRouted(exchange) || (!"http".equals(scheme) && !"https".equals(scheme))) {return chain.filter(exchange); } setAlreadyRouted(exchange); ........省略部分代码..........ServerHttpRequestrequest= exchange.getRequest();finalHttpMethodmethod= HttpMethod.valueOf(request.getMethodValue());finalStringurl= requestUrl.toASCIIString(); Routeroute= exchange.getAttribute(GATEWAY_ROUTE_ATTR);//getHttpClient返回的就是httpClient,这里将请求发送给后端微服务执行 Flux<HttpClientResponse> responseFlux = getHttpClient(route, exchange) .request(method).uri(url).send((req, nettyOutbound) -> { return nettyOutbound.send(request.getBody().map(this::getByteBuf)); }); //交给过滤器链的下一个过滤器执行return responseFlux.then(chain.filter(exchange)); }}

getOrder方法可以看到这个过滤器是排在过滤器链的最后面。filter方法首先获取业务服务真正的请求requestUrl,再用getHttpClient(route, exchange)获取到httpClient对象,接着调用httpClient向业务服务发送请求,将返回结果交由过滤器链的下一个过滤器来执行。

再看看NettyWriteResponseFilter的代码:

java

复制代码

publicclassNettyWriteResponseFilterimplementsGlobalFilter, Ordered { @OverridepublicintgetOrder() {//在过滤器链中的位置还是比较靠前的return -1; }@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //chain.filter(exchange).then表示先交由过滤器链执行后面的过滤器,等后面的执行完成后再执行then后面的代码,相当于After Filter的效果return chain.filter(exchange) .then(Mono.defer(() -> {//获取ConnectionConnectionconnection= exchange.getAttribute(CLIENT_RESPONSE_CONN_ATTR);if (connection == null) {return Mono.empty(); }//获取ServerHttpResponseServerHttpResponseresponse= exchange.getResponse();//获取数据并转换数据final Flux<DataBuffer> body = connection .inbound() .receive() .retain() .map(byteBuf -> wrap(byteBuf, response));MediaTypecontentType= response.getHeaders().getContentType(); //writeAndFlushWith或writeWith都是将返回数据写回客户端return (isStreamingMediaType(contentType) ? response.writeAndFlushWith(body.map(Flux::just)) : response.writeWith(body)); })); }}

getOrder的代码可以看到这个过滤器在过滤器链比较靠前的位置。filter方法,chain.filter(exchange).then表示先交由过滤器链执行后面的过滤器,等后面的执行完成后再执行then后面的代码,相当于After Filter的效果。then后面的代码首先获取Connection和ServerHttpResponse,接着获取返回业务请求返回数据并转换后的body,最后通过/writeAndFlushWith或writeWith都是将返回数据写回客户端。

相关推荐

俄罗斯的 HTTPS 也要被废了?(俄罗斯网站关闭)

发布该推文的ScottHelme是一名黑客,SecurityHeaders和ReportUri的创始人、Pluralsight作者、BBC常驻黑客。他表示,CAs现在似乎正在停止为俄罗斯域名颁发...

如何强制所有流量使用 HTTPS一网上用户

如何强制所有流量使用HTTPS一网上用户使用.htaccess强制流量到https的最常见方法可能是使用.htaccess重定向请求。.htaccess是一个简单的文本文件,简称为“.h...

https和http的区别(https和http有何区别)

“HTTPS和HTTP都是数据传输的应用层协议,区别在于HTTPS比HTTP安全”。区别在哪里,我们接着往下看:...

快码住!带你十分钟搞懂HTTP与HTTPS协议及请求的区别

什么是协议?网络协议是计算机之间为了实现网络通信从而达成的一种“约定”或“规则”,正是因为这个“规则”的存在,不同厂商的生产设备、及不同操作系统组成的计算机之间,才可以实现通信。简单来说,计算机与网络...

简述HTTPS工作原理(简述https原理,以及与http的区别)

https是在http协议的基础上加了一层SSL(由网景公司开发),加密由ssl实现,它的目的是为用户提供对网站服务器的身份认证(需要CA),以至于保护交换数据的隐私和完整性,原理如图示。1、客户端发...

21、HTTPS 有几次握手和挥手?HTTPS 的原理什么是(高薪 常问)

HTTPS是3次握手和4次挥手,和HTTP是一样的。HTTPS的原理...

一次安全可靠的通信——HTTPS原理

为什么HTTPS协议就比HTTP安全呢?一次安全可靠的通信应该包含什么东西呢,这篇文章我会尝试讲清楚这些细节。Alice与Bob的通信...

为什么有的网站没有使用https(为什么有的网站点不开)

有的网站没有使用HTTPS的原因可能涉及多个方面,以下是.com、.top域名的一些见解:服务器性能限制:HTTPS使用公钥加密和私钥解密技术,这要求服务器具备足够的计算能力来处理加解密操作。如果服务...

HTTPS是什么?加密原理和证书。SSL/TLS握手过程

秘钥的产生过程非对称加密...

图解HTTPS「转」(图解http 完整版 彩色版 pdf)

我们都知道HTTPS能够加密信息,以免敏感信息被第三方获取。所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议。...

HTTP 和 HTTPS 有何不同?一文带你全面了解

随着互联网时代的高速发展,Web服务器和客户端之间的安全通信需求也越来越高。HTTP和HTTPS是两种广泛使用的Web通信协议。本文将介绍HTTP和HTTPS的区别,并探讨为什么HTTPS已成为We...

HTTP与HTTPS的区别,详细介绍(http与https有什么区别)

HTTP与HTTPS介绍超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的...

一文让你轻松掌握 HTTPS(https详解)

一文让你轻松掌握HTTPS原文作者:UC国际研发泽原写在最前:欢迎你来到“UC国际技术”公众号,我们将为大家提供与客户端、服务端、算法、测试、数据、前端等相关的高质量技术文章,不限于原创与翻译。...

如何在Spring Boot应用程序上启用HTTPS?

HTTPS是HTTP的安全版本,旨在提供传输层安全性(TLS)[安全套接字层(SSL)的后继产品],这是地址栏中的挂锁图标,用于在Web服务器和浏览器之间建立加密连接。HTTPS加密每个数据包以安全方...

一文彻底搞明白Http以及Https(http0)

早期以信息发布为主的Web1.0时代,HTTP已可以满足绝大部分需要。证书费用、服务器的计算资源都比较昂贵,作为HTTP安全扩展的HTTPS,通常只应用在登录、交易等少数环境中。但随着越来越多的重要...

取消回复欢迎 发表评论: