SpringCloud源码之API网关Gateway
ztj100 2024-10-29 18:19 21 浏览 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都是将返回数据写回客户端。
相关推荐
- 如何将数据仓库迁移到阿里云 AnalyticDB for PostgreSQL
-
阿里云AnalyticDBforPostgreSQL(以下简称ADBPG,即原HybridDBforPostgreSQL)为基于PostgreSQL内核的MPP架构的实时数据仓库服务,可以...
- Python数据分析:探索性分析
-
写在前面如果你忘记了前面的文章,可以看看加深印象:Python数据处理...
- C++基础语法梳理:算法丨十大排序算法(二)
-
本期是C++基础语法分享的第十六节,今天给大家来梳理一下十大排序算法后五个!归并排序...
- C 语言的标准库有哪些
-
C语言的标准库并不是一个单一的实体,而是由一系列头文件(headerfiles)组成的集合。每个头文件声明了一组相关的函数、宏、类型和常量。程序员通过在代码中使用#include<...
- [深度学习] ncnn安装和调用基础教程
-
1介绍ncnn是腾讯开发的一个为手机端极致优化的高性能神经网络前向计算框架,无第三方依赖,跨平台,但是通常都需要protobuf和opencv。ncnn目前已在腾讯多款应用中使用,如QQ,Qzon...
- 用rust实现经典的冒泡排序和快速排序
-
1.假设待排序数组如下letmutarr=[5,3,8,4,2,7,1];...
- ncnn+PPYOLOv2首次结合!全网最详细代码解读来了
-
编辑:好困LRS【新智元导读】今天给大家安利一个宝藏仓库miemiedetection,该仓库集合了PPYOLO、PPYOLOv2、PPYOLOE三个算法pytorch实现三合一,其中的PPYOL...
- C++特性使用建议
-
1.引用参数使用引用替代指针且所有不变的引用参数必须加上const。在C语言中,如果函数需要修改变量的值,参数必须为指针,如...
- Qt4/5升级到Qt6吐血经验总结V202308
-
00:直观总结增加了很多轮子,同时原有模块拆分的也更细致,估计为了方便拓展个管理。把一些过度封装的东西移除了(比如同样的功能有多个函数),保证了只有一个函数执行该功能。把一些Qt5中兼容Qt4的方法废...
- 到底什么是C++11新特性,请看下文
-
C++11是一个比较大的更新,引入了很多新特性,以下是对这些特性的详细解释,帮助您快速理解C++11的内容1.自动类型推导(auto和decltype)...
- 掌握C++11这些特性,代码简洁性、安全性和性能轻松跃升!
-
C++11(又称C++0x)是C++编程语言的一次重大更新,引入了许多新特性,显著提升了代码简洁性、安全性和性能。以下是主要特性的分类介绍及示例:一、核心语言特性1.自动类型推导(auto)编译器自...
- 经典算法——凸包算法
-
凸包算法(ConvexHull)一、概念与问题描述凸包是指在平面上给定一组点,找到包含这些点的最小面积或最小周长的凸多边形。这个多边形没有任何内凹部分,即从一个多边形内的任意一点画一条线到多边形边界...
- 一起学习c++11——c++11中的新增的容器
-
c++11新增的容器1:array当时的初衷是希望提供一个在栈上分配的,定长数组,而且可以使用stl中的模板算法。array的用法如下:#include<string>#includ...
- C++ 编程中的一些最佳实践
-
1.遵循代码简洁原则尽量避免冗余代码,通过模块化设计、清晰的命名和良好的结构,让代码更易于阅读和维护...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- node卸载 (33)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- exceptionininitializererror (33)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)