SpringCloud升级之路2020.0.x版-41. SpringCloudGateway 基本流程讲解(3)
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent
我们继续分析上一节提到的 WebHandler
。加入 Spring Cloud Sleuth 以及 Prometheus 相关依赖之后, Spring Cloud Gateway 的处理流程如下所示:
Spring Cloud Gateway 入口 -> WebFlux 的 DefaultWebFilterChain
Spring Cloud Gateway 是基于 Spring WebFlux 开发的异步响应式网关,异步响应式代码比较难以理解和阅读,我这里给大家分享一种方法去理解,通过这个流程来理解 Spring Cloud Gateway 的工作流程以及底层原理。其实可以理解为,上图这个流程,就是拼出来一个完整的 Mono(或者 Flux)流,最后 subscribe 执行。
当收到一个请求的时候,会经过 org.springframework.web.server.handler.DefaultWebFilterChain
,这是 WebFilter 的调用链,这个链路包括三个 WebFilter:
org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter
:添加 Prometheus 相关依赖之后,会有这个 MetricsWebFilter,用于记录请求处理耗时,采集相关指标。org.springframework.cloud.sleuth.instrument.web.TraceWebFilter
:添加 Spring Cloud Sleuth 相关依赖之后,会有这个 TraceWebFilter。org.springframework.cloud.gateway.handler.predicate.WeightCalculatorWebFilter
:Spring Cloud Gateway 路由权重相关配置功能相关实现类,这个我们这里不关心。
在这个 DefaultWebFilterChain
会形成这样一个 Mono,我们依次将他们标记出来,首先是入口代码 org.springframework.web.server.handler.DefaultWebFilterChain#filter
:
public Mono<Void> filter(ServerWebExchange exchange) {return Mono.defer(() ->// this.currentFilter != null 代表 WebFilter 链还没有结束// this.chain != null 代表 WebFilter 链不为空this.currentFilter != null && this.chain != null ?//在 WebFilter 链没有结束的情况下,调用 WebFilterinvokeFilter(this.currentFilter, this.chain, exchange) ://在 WebFilter 结束的情况下,调用 handler this.handler.handle(exchange));
}
对于我们这里的 WebFilter 链的第一个 MetricsWebFilter
,假设启用了对应的采集统计的话,这时候生成的 Mono 就是:
return Mono.defer(() ->chain.filter(exchange).transformDeferred((call) -> {long start = System.nanoTime();return call//成功时,记录响应时间.doOnSuccess((done) -> MetricsWebFilter.this.onSuccess(exchange, start))//失败时,记录响应时间和异常.doOnError((cause) -> MetricsWebFilter.this.onError(exchange, start, cause));});
);
这里为了方便,我们对代码做了简化,由于我们要将整个链路的所有 Mono 和 Flux 拼接在一起行程完整链路,所以原本是 MetricsWebFilter
中的 onSuccess(exchange, start)
方法,被改成了 MetricsWebFilter.this.onSuccess(exchange, start)
这种伪代码。
接着,根据DefaultWebFilterChain
的源码分析,chain.filter(exchange)
会继续 WebFilter 链路,到达下一个 WebFilter,即 TraceWebFilter
。经过 TraceWebFilter
,Mono 就会变成:
return Mono.defer(() ->new MonoWebFilterTrace(source, chain.filter(exchange), TraceWebFilter.this.isTracePresent(), TraceWebFilter.this, TraceWebFilter.this.spanFromContextRetriever()).transformDeferred((call) -> {//MetricsWebFilter 相关的处理,在前面的代码中给出了,这里省略});
);
可以看出,在 TraceWebFilter
中,整个内部 Mono (chain.filter(exchange)
后续的结果)都被封装成了一个 MonoWebFilterTrace
,这也是保持链路追踪信息的关键实现。
继续 WebFilter 链路,经过最后一个 WebFilter WeightCalculatorWebFilter
; 这个 WebFilter 我们不关心,里面对路由权重做了一些计算操作,我们这里直接忽略即可。这样我们就走完了所有 WebFilter 链路,来到了最后的调用 DefaultWebFilterChain.this.handler
,这个 handler 就是 org.springframework.web.reactive.DispatcherHandler
。在 DispatcherHandler 中,我们会计算出路由并发送请求到符合条件的 GatewayFilter。经过 DispatcherHandler,Mono 会变成:
return Mono.defer(() ->new MonoWebFilterTrace(source, Flux.fromIterable(DispatcherHandler.this.handlerMappings) //读取所有的 handlerMappings.concatMap(mapping -> mapping.getHandler(exchange)) //按顺序调用所有的 handlerMappings 的 getHandler 方法,如果有对应的 Handler 会返回,否则返回 Mono.empty();.next() //找到第一个返回不是 Mono.empty() 的 Handler.switchIfEmpty(DispatcherHandler.this.createNotFoundError()) //如果没有返回不为 Mono.empty() 的 handlerMapping,则直接返回 404.flatMap(handler -> DispatcherHandler.this.invokeHandler(exchange, handler)) //调用对应的 Handler.flatMap(result -> DispatcherHandler.this.handleResult(exchange, result)), //处理结果TraceWebFilter.this.isTracePresent(), TraceWebFilter.this, TraceWebFilter.this.spanFromContextRetriever()).transformDeferred((call) -> {//MetricsWebFilter 相关的处理,在前面的代码中给出了,这里省略});
);
handlerMappings 包括:
org.springframework.boot.actuate.endpoint.web.reactive.WebFluxEndPointHandlerMapping
:由于我们项目中添加了 Actuator 相关依赖,所以这里有这个 HandlerMapping。Actuator 相关路径映射,不是我们这里关心的。但是可以看出,Actuator 相关路径优先于 Spring Cloud Gateway 配置路由org.springframework.boot.actuate.endpoint.web.reactive.ControllerEndpointHandlerMapping
:由于我们项目中添加了 Actuator 相关依赖,所以这里有这个 HandlerMapping。使用 @ControllerEndpoint 或者 @RestControllerEndpoint 注解标注的 Actuator 相关路径映射,不是我们这里关心的。org.springframework.web.reactive.function.server.support.RouterFunctionMapping
:在 Spring-WebFlux 中,你可以定义很多不同的 RouterFunction 来控制路径路由,但这也不是我们这里关心的。但是可以看出,自定义的 RouterFunction 会优先于 Spring Cloud Gateway 配置路由org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping
:针对 @RequestMapping 注解的路径的 HandlerMapping,不是我们这里关心的。但是可以看出,如果你在 Spring Cloud Gateway 中指定 RequestMapping 路径,会优先于 Spring Cloud Gateway 配置路由。org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping
:这个是 Spring Cloud Gateway 的 HandlerMapping,会读取 Spring Cloud Gateway 配置并生成路由。这个是我们这里要详细分析的。
其实这些 handlerMappings,我们这里肯定走的是 RoutePredicateHandlerMapping
的相关逻辑,所以我们的 Mono 又可以简化成:
return Mono.defer(() ->new MonoWebFilterTrace(source, RoutePredicateHandlerMapping.this.getHandler(exchange).switchIfEmpty(DispatcherHandler.this.createNotFoundError()) //如果没有返回不为 Mono.empty() 的 handlerMapping,则直接返回 404.flatMap(handler -> DispatcherHandler.this.invokeHandler(exchange, handler)) //调用对应的 Handler.flatMap(result -> DispatcherHandler.this.handleResult(exchange, result)), //处理结果TraceWebFilter.this.isTracePresent(), TraceWebFilter.this, TraceWebFilter.this.spanFromContextRetriever()).transformDeferred((call) -> {//MetricsWebFilter 相关的处理,在前面的代码中给出了,这里省略});
);
我们来看 RoutePredicateHandlerMapping
,首先这些 handlerMapping 都是继承了抽象类 org.springframework.web.reactive.handler.AbstractHandlerMapping
, 前面我们拼接的 Mono 里面的 getHandler 的实现其实就在这个抽象类中:
public Mono<Object> getHandler(ServerWebExchange exchange) {//调用抽象方法 getHandlerInternal 获取真正的 Handlerreturn getHandlerInternal(exchange).map(handler -> {//这里针对 handler 做一些日志记录if (logger.isDebugEnabled()) {logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);}// 跨域处理ServerHttpRequest request = exchange.getRequest();if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {CorsConfiguration config = (this.corsConfigurationSource != null ?this.corsConfigurationSource.getCorsConfiguration(exchange) : null);CorsConfiguration handlerConfig = getCorsConfiguration(handler, exchange);config = (config != null ? config.combine(handlerConfig) : handlerConfig);if (config != null) {config.validateAllowCredentials();}if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {return NO_OP_HANDLER;}}return handler;});
}
可以看出,其实核心就是每个实现类的 getHandlerInternal(exchange)
方法,所以在我们拼接的 Mono 中,我们会忽略抽象类中的针对 handler 之后的 map 处理。
return Mono.defer(() ->new MonoWebFilterTrace(source, RoutePredicateHandlerMapping.this.getHandlerInternal(exchange).switchIfEmpty(DispatcherHandler.this.createNotFoundError()) //如果没有返回不为 Mono.empty() 的 handlerMapping,则直接返回 404.flatMap(handler -> DispatcherHandler.this.invokeHandler(exchange, handler)) //调用对应的 Handler.flatMap(result -> DispatcherHandler.this.handleResult(exchange, result)), //处理结果TraceWebFilter.this.isTracePresent(), TraceWebFilter.this, TraceWebFilter.this.spanFromContextRetriever()).transformDeferred((call) -> {//MetricsWebFilter 相关的处理,在前面的代码中给出了,这里省略});
);
接下来经过 RoutePredicateHandlerMapping
的 getHandlerInternal(exchange)
方法,我们的 Mono 变成了:
return Mono.defer(() ->new MonoWebFilterTrace(source, RoutePredicateHandlerMapping.this.lookupRoute(exchange) //根据请求寻找路由.flatMap((Function<Route, Mono<?>>) r -> {exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r); //将路由放入 Attributes 中,后面我们还会用到return Mono.just(RoutePredicateHandlerMapping.this.webHandler); //返回 RoutePredicateHandlerMapping 的 FilteringWebHandler}).switchIfEmpty( //如果为 Mono.empty(),也就是没找到路由Mono.empty() //返回 Mono.empty().then(Mono.fromRunnable(() -> { //返回 Mono.empty() 之后,记录日志if (logger.isTraceEnabled()) {logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");}}))).switchIfEmpty(DispatcherHandler.this.createNotFoundError()) //如果没有返回不为 Mono.empty() 的 handlerMapping,则直接返回 404.flatMap(handler -> DispatcherHandler.this.invokeHandler(exchange, handler)) //调用对应的 Handler.flatMap(result -> DispatcherHandler.this.handleResult(exchange, result)), //处理结果TraceWebFilter.this.isTracePresent(), TraceWebFilter.this, TraceWebFilter.this.spanFromContextRetriever()).transformDeferred((call) -> {//MetricsWebFilter 相关的处理,在前面的代码中给出了,这里省略});
);
RoutePredicateHandlerMapping.this.lookupRoute(exchange)
根据请求寻找路由,这个我们就不详细展开了,其实就是根据你的 Spring Cloud Gateway 配置,找到合适的路由。接下来我们来看调用对应的 Handler,即 FilteringWebHandler。DispatcherHandler.this.invokeHandler(exchange, handler)
我们这里也不详细展开,我们知道其实就是调用 Handler 的 handle 方法,即 FilteringWebHandler 的 handle 方法,所以 我们的 Mono 变成了:
return Mono.defer(() ->new MonoWebFilterTrace(source, RoutePredicateHandlerMapping.this.lookupRoute(exchange) //根据请求寻找路由.flatMap((Function<Route, Mono<?>>) r -> {exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r); //将路由放入 Attributes 中,后面我们还会用到return Mono.just(RoutePredicateHandlerMapping.this.webHandler); //返回 RoutePredicateHandlerMapping 的 FilteringWebHandler}).switchIfEmpty( //如果为 Mono.empty(),也就是没找到路由Mono.empty() .then(Mono.fromRunnable(() -> { //返回 Mono.empty() 之后,记录日志if (logger.isTraceEnabled()) {logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");}}))).switchIfEmpty(DispatcherHandler.this.createNotFoundError()) //如果没有返回不为 Mono.empty() 的 handlerMapping,则直接返回 404.then(FilteringWebHandler.this.handle(exchange).then(Mono.empty())) //调用对应的 Handler.flatMap(result -> DispatcherHandler.this.handleResult(exchange, result)), //处理结果TraceWebFilter.this.isTracePresent(), TraceWebFilter.this, TraceWebFilter.this.spanFromContextRetriever()).transformDeferred((call) -> {//MetricsWebFilter 相关的处理,在前面的代码中给出了,这里省略});
);
由于调用对应的 Handler,最后返回的是 Mono.empty()
,所以后面的 flatMap 其实不会执行了。所以我们可以将最后的处理结果这一步去掉。所以我们的 Mono 就变成了:
return Mono.defer(() ->new MonoWebFilterTrace(source, RoutePredicateHandlerMapping.this.lookupRoute(exchange) //根据请求寻找路由.flatMap((Function<Route, Mono<?>>) r -> {exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r); //将路由放入 Attributes 中,后面我们还会用到return Mono.just(RoutePredicateHandlerMapping.this.webHandler); //返回 RoutePredicateHandlerMapping 的 FilteringWebHandler}).switchIfEmpty( //如果为 Mono.empty(),也就是没找到路由Mono.empty() .then(Mono.fromRunnable(() -> { //返回 Mono.empty() 之后,记录日志if (logger.isTraceEnabled()) {logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");}}))).switchIfEmpty(DispatcherHandler.this.createNotFoundError()) //如果没有返回不为 Mono.empty() 的 handlerMapping,则直接返回 404.then(FilteringWebHandler.this.handle(exchange).then(Mono.empty()))), //调用对应的 HandlerTraceWebFilter.this.isTracePresent(), TraceWebFilter.this, TraceWebFilter.this.spanFromContextRetriever()).transformDeferred((call) -> {//MetricsWebFilter 相关的处理,在前面的代码中给出了,这里省略});
);
FilteringWebHandler.this.handle(exchange)
其实就是从 Attributes 中取出路由,从路由中取出对应的 GatewayFilters,与全局 GatewayFilters 放到同一个 List 中,并按照这些 GatewayFilter 的顺序排序(可以通过实现 org.springframework.core.Ordered
接口来制定顺序),然后生成 DefaultGatewayFilterChain
即 GatewayFilter 链路。对应的源码是:
public Mono<Void> handle(ServerWebExchange exchange) {//从 Attributes 中取出路由,从路由中取出对应的 GatewayFiltersRoute route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);List<GatewayFilter> gatewayFilters = route.getFilters();//与全局 GatewayFilters 放到同一个 List 中List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);combined.addAll(gatewayFilters);//按照这些 GatewayFilter 的顺序排序(可以通过实现 `org.springframework.core.Ordered` 接口来制定顺序)AnnotationAwareOrderComparator.sort(combined);if (logger.isDebugEnabled()) {logger.debug("Sorted gatewayFilterFactories: " + combined);}//生成调用链return new DefaultGatewayFilterChain(combined).filter(exchange);
}
这个 GatewayFilter 调用链和 WebFilter 调用链类似,参考 DefaultGatewayFilterChain
的源码:
public Mono<Void> filter(ServerWebExchange exchange) {return Mono.defer(() -> {//如果链路没有结束,则继续链路if (this.index < filters.size()) {GatewayFilter filter = filters.get(this.index);//这里将 index + 1,也就是调用链路中的下一个 GatewayFilterDefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1);//每个 filter 中如果想要继续链路,则会调用 chain.filter(exchange),这也是我们开发 GatewayFilter 的时候的使用方式return filter.filter(exchange, chain);}else {//到达末尾,链路结束return Mono.empty(); // complete}});
}
所以,经过 DefaultGatewayFilterChain
后,我们的 Mono 就会变成:
return Mono.defer(() ->new MonoWebFilterTrace(source, RoutePredicateHandlerMapping.this.lookupRoute(exchange) //根据请求寻找路由.flatMap((Function<Route, Mono<?>>) r -> {exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r); //将路由放入 Attributes 中,后面我们还会用到return Mono.just(RoutePredicateHandlerMapping.this.webHandler); //返回 RoutePredicateHandlerMapping 的 FilteringWebHandler}).switchIfEmpty( //如果为 Mono.empty(),也就是没找到路由Mono.empty() .then(Mono.fromRunnable(() -> { //返回 Mono.empty() 之后,记录日志if (logger.isTraceEnabled()) {logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");}}))).switchIfEmpty(DispatcherHandler.this.createNotFoundError()) //如果没有返回不为 Mono.empty() 的 handlerMapping,则直接返回 404.then(new DefaultGatewayFilterChain(combined).filter(exchange).then(Mono.empty()))), //调用对应的 HandlerTraceWebFilter.this.isTracePresent(), TraceWebFilter.this, TraceWebFilter.this.spanFromContextRetriever()).transformDeferred((call) -> {//MetricsWebFilter 相关的处理,在前面的代码中给出了,这里省略});
);
再继续展开 DefaultGatewayFilterChain
的链路调用,可以得到:
return Mono.defer(() ->new MonoWebFilterTrace(source, RoutePredicateHandlerMapping.this.lookupRoute(exchange) //根据请求寻找路由.flatMap((Function<Route, Mono<?>>) r -> {exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r); //将路由放入 Attributes 中,后面我们还会用到return Mono.just(RoutePredicateHandlerMapping.this.webHandler); //返回 RoutePredicateHandlerMapping 的 FilteringWebHandler}).switchIfEmpty( //如果为 Mono.empty(),也就是没找到路由Mono.empty() .then(Mono.fromRunnable(() -> { //返回 Mono.empty() 之后,记录日志if (logger.isTraceEnabled()) {logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");}}))).switchIfEmpty(DispatcherHandler.this.createNotFoundError()) //如果没有返回不为 Mono.empty() 的 handlerMapping,则直接返回 404.then(Mono.defer(() -> {//如果链路没有结束,则继续链路if (DefaultGatewayFilterChain.this.index < DefaultGatewayFilterChain.this.filters.size()) {GatewayFilter filter = DefaultGatewayFilterChain.this.filters.get(DefaultGatewayFilterChain.this.index);//这里将 index + 1,也就是调用链路中的下一个 GatewayFilterDefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(DefaultGatewayFilterChain.this, DefaultGatewayFilterChain.this.index + 1);//每个 filter 中如果想要继续链路,则会调用 chain.filter(exchange),这也是我们开发 GatewayFilter 的时候的使用方式return filter.filter(exchange, chain);}else {return Mono.empty(); //链路完成}}).then(Mono.empty()))), //调用对应的 HandlerTraceWebFilter.this.isTracePresent(), TraceWebFilter.this, TraceWebFilter.this.spanFromContextRetriever()).transformDeferred((call) -> {//MetricsWebFilter 相关的处理,在前面的代码中给出了,这里省略});
);
这样,就形成了 Spring Cloud Gateway 针对路由请求的完整 Mono 调用链。
微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer:
SpringCloud升级之路2020.0.x版-41. SpringCloudGateway 基本流程讲解(3)相关推荐
- SpringCloud升级之路2020.0.x版-43.为何 SpringCloudGateway 中会有链路信息丢失
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 在开始编写我们自己的日志 Filter 之前,还有一个问题我想在这里和大家分享,即在 Sp ...
- SpringCloud升级之路2020.0.x版-26.OpenFeign的组件
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 首先,我们给出官方文档中的组件结构图: [外链图片转存失败,源站可能有防盗链机制,建议将图 ...
- SpringCloud升级之路2020.0.x版-13.UnderTow 核心配置
本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford Undertow ...
- SpringCloud升级之路2020.0.x版-12.UnderTow 简介与内部原理
本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford 在我们的项目中,我 ...
- 2021-08-05SpringCloud升级之路2020.0.x版-5.所有项目的parent与spring-framework-common说明
本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford 源代码文件:htt ...
- Spring Cloud 升级之路 - 2020.0.x - 1. 背景知识、需求描述与公共依赖
1. 背景知识.需求描述与公共依赖 1.1. 背景知识 & 需求描述 Spring Cloud 官方文档说了,它是一个完整的微服务体系,用户可以通过使用 Spring Cloud 快速搭建一个 ...
- note8 升级android9,三星note8 N9500一键ADB升级One UI 9.0内测版
三星note8 N9500一键ADB升级One UI 9.0内测版是一款三星N9500 ADB一键升级9.0底包和刷机教程,三星 note8 安卓Pie,One UI .自己动手丰衣足食,抢不到官方资 ...
- 三星android安卓版本怎么升级,三星更改安卓9.0升级计划三款机型将可以升级到安卓9.0正式版...
描述 近日有网友发现三星中国更新了安卓9.0系统升级公告,和本月初那个公告相比,最明显的变化是国行Galaxy S9.S9+以及Galaxy Note9的升级时间提前到2019年1月,而原本计划是今年 ...
- 华为 android 5.0系统下载地址,华为emui5.0升级公告-emui 5.0官方版下载v5.0 官方最新版-西西软件下载...
emui5.0是关于华为最新的开发的一个手机的系统,对比其他的安卓系统来说,emui5.0的使用的界面可以说是十分的简洁,而且使用起来的体验也是十分的流畅,让用户能够享受到一个很不错的操作系统的体验, ...
最新文章
- python 把int类型转bytes以及把bytes 转int 类型(使用方法to_bytes ,from_byte, struct)
- for语句内嵌例题与个人理解
- vs 中大括号之间垂直虚线显示
- 导出Windows服务器下的Oracle数据库并导入到Linux服务器下的Oracle数据库中
- jQuery动画的显示与隐藏效果!
- php mysql读取数组_php实现通用的从数据库表读取数据到数组的函数实例
- SAP ABAP Platform 1909最新版的 docker 镜像
- 1019 数字黑洞 (20)
- Atitit. 构造ast 语法树的总结attilax oao
- 儿童素描手绘创意设计字体 for mac
- 32 Pin和 8 Pin(引脚 )flash烧录操作指导
- Activiti流程引擎与业务整合方案
- HTML5软件设计大赛,我院成功举行第十七届山东省大学生软件设计大赛 HTML5创意应用命题决赛...
- Flutter入门综合练习
- 如何判断是否被职场PUA探针及处理
- 小米 note3 android,小米NOTE3 MIUI9.6稳定版 开启全面屏手势和屏蔽经典物理按键全攻略...
- python terminal 库_zhihu-terminal 终端版知乎客户端
- 想报考pmp(pmp报考费),一定要先经过培训机构吗?
- VoIP通话-基于SIP协议的Asterisk(一)-实现流程
- python中idx+=1_在Python中为apos;循环访问索引 Dovov编程网