源码深度剖析Spring Cloud Gateway如何处理一个请求(只能那么细了)【云原生】

网友投稿 318 2022-08-23

源码深度剖析Spring Cloud Gateway如何处理一个请求(只能那么细了)【云原生】

文章目录

​​一、前言​​​​二、Spring Cloud Gateway的由来​​

​​三个关键模块​​

​​三、Spring Cloud Gateway执行流程​​

​​更细粒度的执行流程​​

​​四、调试程序信息​​​​五、Gateway处理请求的流程分析​​

​​1、接收请求的入口​​

​​ReactorHttpHandlerAdapter实例化的时机​​

​​2、HttpHandler开始解析请求​​​​3、Web过滤器链WebFilterChain​​​​4、真正处理请求的入口DispatcherHandler​​

​​1)RouterFunctionMapping默认啥也不做​​​​2)RequestMappingHandlerMapping默认啥也不做​​​​3)RoutePredicateHandlerMapping获取Route路由规则​​

​​1> 不符合谓词Predicate的路由​​​​2> 符合谓词Predicate的路由​​​​3>路由匹配完之后返回FilteringWebHandler​​

​​4)SimpleUrlHandlerMapping啥也不做​​

​​5、核心过滤器链FilterWebHandler执行​​

​​1)从DispatcherHandler到FilterWebHandler的流转​​​​2)FilteringWebHandler的工作​​

​​6、请求转发到特定服务​​

​​1)请求负载均衡?​​​​2)请求执行 <--NettyRoutingFilter​​​​3)请求转发 <--ForwardRoutingFilter​​

​​7、响应回写​​

​​总结​​

一、前言

至此微服务网关系列文章已出:

​​【云原生&微服务>SCG网关篇一】为什么要有网关、生产环境如何选择网关​​​​云原生&微服务>SCG网关篇二】生产上那些灰度发布方式​​​​【云原生&微服务>SCG网关篇三】Spring Cloud Gateway是什么、详细使用案例​​​​云原生&微服务>SCG网关篇四】Spring Cloud Gateway内置的11种PredicateFactory如何使用​​​​【云原生&微服务>SCG网关篇五】Spring Cloud Gateway自定义PredicateFactory​​​​【云原生&微服务>SCG网关篇六】Spring Cloud Gateway内置的18种Filter使用姿势​​​​【云原生&微服务>SCG网关篇七】Spring Cloud Gateway基于内置Filter实现限流、熔断、重试​​​​【云原生&微服务>SCG网关篇八】Spring Cloud Gateway三种自定义Filter、GlobalFilter的方式​​​​【云原生&微服务>SCG网关篇九】Spring Cloud Gateway集成Nacos详细案例​​​​【云原生&微服务>SCG网关篇十】Spring Cloud Gateway集成Actuator、Zipkin详细案例​​​​【云原生&微服务>SCG网关篇十一】Spring Cloud Gateway解决跨域问题​​​​【云原生&微服务>SCG网关篇十二】Spring Cloud Gateway集成Sentinel API实现多种限流方式​​

聊了以下问题:

为什么要有网关?网关的作用是什么?网关的分类?网关的技术选型?使用网关时常用的灰度发布方式有哪些?Spring Cloud Gateway是什么?详细使用案例?Spring Cloud Gateway内置的11种PredicateFactory如何自定义PredicateFactory?Spring Cloud Gateway内置的18种常用的FilterSpring Cloud Gateway基于内置Filter实现限流、熔断、重试Spring Cloud Gateway三种自定义Filter、GlobalFilter的方式Spring Cloud Gateway集成Nacos案例Spring Cloud Gateway集成Actuator、Zipkin案例Spring Cloud Gareway如何解决CORS跨域问题Spring Cloud Gateway集成Sentinel API实现限流

其中很多是Spring Cloud Gateway的使用,此篇文章开始进行Spring Cloud Gateway的源码分析。响应式编程是真的很难调试 / debug,大家注意看我代码流程截图中打的断点,不然自己F7、F8会进入到响应式编程的API中,很难走出来。关于响应式编程的介绍,后续博主有时间会专项出一个系列。

PS:SpringCloud版本信息:

2.4.2 2020.0.1 2021.1 org.springframework.boot spring-boot-dependencies ${spring-boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring-cloud-alibaba.version} pom import

二、Spring Cloud Gateway的由来

Spring Cloud Gateway 是Spring官方推出的一款API网关,其包含Spring5、SpringBoot2、Project Reactor,其中底层通信框架用的netty。Spring Cloud Gateway在推出的时候,Netflix公司已经推出了ZUUL,但是ZUUL有一个很致命的缺点:其通信方式是同步阻塞的,虽然后续升级出了异步非阻塞式的Zuul2;但是由于Spring Cloud Gateway已经推出了一段时间,并且用户反响还不错,而Zuul2自身也面临资料少、维护性较差等因素没有被广泛应用;

三个关键模块

在使用Spring Cloud Gateway的时候有三个核心模块:

Route:

路由是⽹关最基础的部分,路由信息由⼀个ID、⼀个目标URL、⼀组断⾔​​Predicate​​和⼀组Filter组成。如果断⾔路由为真,则说明请求的URL和配置匹配;也可以把路由理解为一条请求转发规则,看做是集URI、predicate、filter等属性的一个元数据类。

Predicate:

​​Predicate​​作为路由的匹配条件,其是Java8函数式编程的一个方法,可以看做是满足什么条件的时候,route规则进行生效。

Filter:

Filter是Spring Cloud Gateway最核心的模块;鉴权、安全、限流、熔断、逻辑执行、网络调用都是Filter来完成的,其中又细分为gateway filter和global filter,区别在于是具体一个route规则生效还是所有route规则都生效。

三者的关联:

三、Spring Cloud Gateway执行流程

上图是Spring Cloud Gateway​​官方文档​​给出的一个工作原理图,Spring Cloud Gateway 接收到请求后进行路由规则的匹配,然后交给web handler 进行处理,web handler 会执行一系列的filter逻辑。

更细粒度的执行流程

四、调试程序信息

调试程序采用博文(​​Spring Cloud Gateway是什么、详细使用案例​​​)中的;另外需要对​​gateway-center​​ 项目的application.yml配置文件做一些调整,如下:

server: port: 9999spring: application: name: nacos-gateway cloud: nacos: discovery: server-addr: 127.0.0.1:8848 gateway: discovery: locator: # 开启从注册中心动态创建路由的功能 enabled: true # 是否使用service-id的小写,默认是大写 lower-case-service-id: true routes: - id: gateway-nacos-service-route # 其中配置的lb://表示从注册中心获取服务,后面的gateway-nacos-provider表示目标服务在注册中心上的服务名 uri: lb://gateway-nacos-provider predicates: - Path=/nacos/** filters: -

上述配置中涉及的内置Predicate参考博文:​​Spring Cloud Gateway内置的11种PredicateFactory如何使用​​。

下面基于请求:​​Gateway采用 ​​Netty Server​​​ 接收到客户端的请求;在接收请求时有一个关键类​​ReactorHttpHandlerAdapter​​,ReactorHttpHandlerAdapter只做一件事:将netty的请求/响应 转换为HTTP的请求/响应 并交给handler 执行后面的逻辑;

ReactorHttpHandlerAdapter实例化的时机

​​ReactorHttpHandlerAdapter​​​是在SpringBoot启动流程中初始化WebServer时实例化的,从​​AbstractApplicationContext#refresh()​​方法开始流程如下:

2、HttpHandler开始解析请求

handler(​​HttpWebHandlerAdapter​​)首先将request 和 response转为一个ServerWebExchange(可以理解为网关的上下文),ServerWebExchange非常核心,它是各个filter之间参数流转的载体,其包含request、response、attributes(扩展字段);然后进行web filter链的执行。

从​​ReactorHttpHandlerAdapter#apply()​​方法接收到请求之后的代码执行链路如下:

到这里进入到了​​HttpWebHandlerAdapter​​​类的​​handle()​​​方法,其负责组装网关的上下文(​​ServerWebExchange​​​),然后将其传递给后续的代理handle(​​ExceptionHandlingWebHandler​​)处理;代码执行流程如下:

最终进入到​​FilteringWebHandler​​中;

3、Web过滤器链WebFilterChain

​​FilteringWebHandler​​​组合了一个Web过滤器链​​WebFilterChain​​;

public class DefaultWebFilterChain implements WebFilterChain { private final List allFilters; private final WebHandler handler; @Nullable private final WebFilter currentFilter; @Nullable private final DefaultWebFilterChain chain;}

Web过滤器链​​WebFilterChain​​​中包含一条filter链(​​WebFilter​​集合),WebFilter主要用于实现横切、应用程序无关的需求,如安全性、超时等;由此可见Spring Cloud Gateway的过滤器设计是层层嵌套,扩展性很强。

就一个最简单的gateway程序而言,Web过滤器链​​WebFilterChain​​​中仅包含一个WebFilter(​​WeightCalculatorWebFilter​​),其用于计算权重;具体代码执行流程如下:

由于​​ServerWebExchange​​​中默认没有权重信息,所以​​WeightCalculatorWebFilter​​​中什么也不做,直接进入Web过滤器链​​WebFilterChain​​​的下一个​​WebFilter​​​,由于Web过滤器链​​WebFilterChain​​​中仅有一个​​WeightCalculatorWebFilter​​,所以下一个WebFilter是null。

在Spring Cloud Gateway中所有的WebFilter过滤操作都体现在​​WebFilterChain​​​接口的实现类​​DefaultWebFilterChain​​#filter()方法,所以进到如下代码中:

三元表达式的逻辑为:如果WebFilterChain中的当前WebFilter和组合的webFilterChain都不为空,则执行当前WebFilter,否则进入DispatcherHandler#handle()方法处理请求;

此时,currentFilter 和 chain均已为空,接下来会直接进入到DispatcherHandler中;

4、真正处理请求的入口DispatcherHandler

Spring Cloud gateway处理请求的入口类为DispatcherHandler,其负责请求的分发;DispatcherHandler可以和 Spring Mvc的 DispacherServlet,Spring MVC执行流程参考博文:​​图文源码分析Spring MVC请求执行流程​​。

DispatcherHandler#handle()方法逻辑如下:

DispatcherHandler 首先判断持有的handerMappings是否为空,为空则调用 createNotFoundError方法,返回 No matching handler信息;接着有序遍历 handerMappings并调用 mapping的getHandler()方法返回mapping对应的handler。

DispatcherHandler持有四个HandlerMapping:RouterFunctionMapping、RequestMappingHandlerMapping、RoutePredicateHandlerMapping、SimpleUrlHandlerMapping。

下面接着来看这四个HandlerMapping分别对请求做了哪些处理?

这四个HandlerMapping都实现接口​​AbstractHandlerMapping​​​,所以调用其getHandler()方法都会进入到​​AbstractHandlerMapping#getHandler()​​​方法,进而调用每个HandlerMapping自己实现的​​getHandlerInternal()​​方法:

1)RouterFunctionMapping默认啥也不做

请求走到RouterFunctionMapping的getHandlerInternal()方法,代码执行流程如下:

默认​​RouterFunction​​为null,直接返回 Mono.empty();

2)RequestMappingHandlerMapping默认啥也不做

RequestMappingHandlerMapping的类图如下:

由于RequestMappingHandlerMapping中没有重写getHandlerInternal()方法,所以请求达到其父类​​RequestMappingInfoHandlerMapping​​的getHandlerInternal()方法,代码执行流程如下:

这里本质上和RouterFunctionMapping一样都是直接返回 Mono.empty();

3)RoutePredicateHandlerMapping获取Route路由规则

RoutePredicateHandlerMapping是做路由匹配的核心类,部分代码执行流程如下:

RoutePredicateHandlerMapping#getHandlerInternal()方法中首先会调用​​lookupRoute()​​方法获取到所有的Route,代码执行流程如下:

方法中调用routeLocator.getRoutes()获取到所有的Route,然后针对请求遍历每个Route做Predicate路由规则匹配,找到与请求匹配的路由;

根据示例,这里会找到两个Route路由:

1> 不符合谓词Predicate的路由

请求访问的请求路径是​​/gateway/simple-service/hello/sayHello​​​;而Route(​​ingredients-fallback​​​)做匹配的谓词工厂是​​PathRoutePredicateFactory​​​,匹配路径为:​​/fallback​​;

下面为具体的代码执行流程:

请求路径​​/gateway/simple-service/hello/sayHello​​​和Predicate路径​​/fallback​​不匹配,返回false,表示路由的谓词不匹配。

2> 符合谓词Predicate的路由

请求访问的请求路径是​​/gateway/simple-service/hello/sayHello​​​;Route(​​my_route​​​)做匹配的谓词工厂是​​PathRoutePredicateFactory​​​,匹配路径为:​​/gateway/simple-service/**​​;

下面为具体的代码执行流程:

请求路径​​/gateway/simple-service/hello/sayHello​​​和Predicate路径​​/gateway/simple-service/**​​匹配,返回true,表示路由的谓词匹配。

3>路由匹配完之后返回FilteringWebHandler

4)SimpleUrlHandlerMapping啥也不做

由于RoutePredicateHandlerMapping中已经获取到了FilteringWebHandler,所以不会走进SimpleUrlHandlerMapping,而是至今后续的FilterWebHandler的执行;

5、核心过滤器链FilterWebHandler执行

1)从DispatcherHandler到FilterWebHandler的流转

找到路由规则后,将执行过滤器链的核心类​​FilteringWebHandler​​返回到DispatcherHandler中,代码执行流程如下:

DispatcherHandler的invokHandler()方法中首先会获取到四个HandlerAdapter,分别为:WebSocketHandlerAdapter、RequestMappingHandlerAdapter、handlerFunctionAdapter、SimpleHandlerAdapter;

然后遍历这个四个HandlerAdapter,判断其是否支持​​FilterWebHandler​​​的执行;最后仅有​​SimpleHandlerAdapter​​​支持​​FilterWebHandler​​的执行:

​​SimplerHandlerAdapter#handle()​​​方法中直接调用的​​FilteringWebHandler#handle()​​方法;

这里之所以可以直接把FilteringWebHandler强转为WebHandler,是因为FilteringWebHandler实现自WebHandler接口;

2)FilteringWebHandler的工作

进入到​​FilteringWebHandler#handle()​​方法看看都做了些什么;

FilteringWebHandler中做的事很简单,四件事:

看一下过滤器链中有哪些过滤器:

一共11个过滤器,针对Route(​​my_route​​​),本文使用了两个Filter(Gateway内嵌的​​StripePrefix​​​和我们自定义的​​MyLog​​);其余9个全是Gateway自带的GlobalFilter。

以自定义的​​MyLog​​为例,来看一下Filter是如何执行的?

看到这,博主想了一个问题,gateway是何时 怎么将请求转发到具体的服务的?

6、请求转发到特定服务

上面我们看到过滤器链​​DefaultGatewayFilterChain​​中有11个Filter,其中有9个是GlobalFilter:

1)请求负载均衡?

我们可以注意到有一个过滤器叫​​NoLoadBalancerClientFilter​​(GatewayNoLoadBalancerClientAutoConfiguration的内部类),从它的命名来看就感觉它和负载均衡可能有点关系,进入它的代码执行逻辑验证一下:

看到最后这个​​lb​​​字符串,大家应该感觉很熟悉了,在我们使用服务注册中心,Route中配置的uri往往是​​lb://​​为前缀,例如:

由于这里我们的URI是直接指定的IP:post地址,所以不会走负载均衡;关于使用了服务注册中心之后如何做负载均衡,博主后面再出一篇文章。

2)请求执行 <–NettyRoutingFilter

当请求​​scheme​​​是以​​或 ​​Netty Proxy(说白了就是一个HttpClient)打到指定的HTTP服务上;

3)请求转发 <–ForwardRoutingFilter

当需要对请求进行forward时,请求转发由全局过滤器ForwardRoutingFilter来执行;ForwardRoutingFilter直接复用了Spring MVC的能力,将请求提交给dispatcherHandler进行处理,dispatcherHandler根据path前缀找到所需要的目标处理器执行逻辑;

7、响应回写

响应回由全局过滤器NettyWriteResponseFilter来执行,但是我们可以注意到执行器链中NettyWriteResponseFilter是排在最前面(除Cache相关Filter之外);咦,按道理来说这种处理响应的过滤器类应该放在比较靠后的位置才对啊!此处的设计就比较有意思,我们可以看到​​chain.filter(exchange).then()​​​这段逻辑,意思就是执行到我当前Filter的时候直接通过​​chain.filter()​​​执行后一个Filter,等后面的过滤器都执行完后再返回执行​​then()​​中逻辑。

这种行为控制的方式还是很值得学习的,往简单了想,这里和Spring的​​BeanPostProcessor​​类的postProcessBeforeInitialization()和postProcessAfterInitialization()类似,强调的是当前逻辑处理之前 / 处理完之后,再做什么事。

总结

当请求过来时:首先由中央处理器DispatcherHandler进行分发,通过它找到对应的RoutePredicateHandlerMapping,进而找到请求匹配的Route、获取到FilteringWebHandler;然后再由FilteringWebHandler组合好过滤器链,通过滤器链处理请求,将请求引导到真实的服务器上进行处理;

总感觉Spring Cloud Gateway更像是一个过滤器链执行框架;因为实际的请求转发 / 响应回写都是在过滤器中做的;此外SpringCloud Gateway内置了很多过滤器,有一些是可以不用的,如果有个卸载过滤器的功能就很哇塞了;当然并不是每个人都可以去随便增减Spring Cloud Gateway内置过滤器的,需要有一定的知识沉淀,否则鬼知道踩什么坑呢。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:【云原生&微服务>SCG网关篇四】Spring Cloud Gateway内置的11种PredicateFactory如何使用
下一篇:把低俗当创意:“辣条一哥”卫龙为何戒不掉“黑红营销”!(卫龙辣条网络营销案例)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~