java系统找不到指定文件怎么解决
372
2022-08-23
源码深度剖析Spring Cloud Gateway如何处理一个请求(只能那么细了)【云原生】
文章目录
一、前言二、Spring Cloud Gateway的由来
三个关键模块
三、Spring Cloud Gateway执行流程
更细粒度的执行流程
四、调试程序信息五、Gateway处理请求的流程分析
1、接收请求的入口
ReactorHttpHandlerAdapter实例化的时机
2、HttpHandler开始解析请求3、Web过滤器链WebFilterChain4、真正处理请求的入口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)请求执行 <--NettyRoutingFilter3)请求转发 <--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版本信息:
二、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
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小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~