参考资料:
Spring Cloud 官网
Zuul wiki相关版本:zuul 1.3.1,spring boot 2.1.4 ,spring cloud Greenwich.SR1
回顾
在 Spring Cloud Zuul【源码篇】揭秘 Zuul 中知道了 Zuul 本质是 Servlet 和 Filter 结合体。
其中 Servlet 用来接收请求,而 ZuulFilter 则对请求进行包装以及处理,如:请求转发,客户端负载均衡,熔断等。
Zuul 过滤链执行顺序
Zuul 提供了一些默认的 ZuulFilter ,有的用来做请求封装,有的用来处理请求。
这些 ZuulFilter 各自有各自的作用,并按照顺序组成过滤链。
过滤链的排序规则
规则一、根据 pre、route、 post route 进行区分。
pre 优先级最高。
route 优先级次之。
post route 优先级最低。规则二、根据规则一进行类别区分后,每一个类别再根据自定义order 进行排序
其中的 order 在每个 ZuulFilter 中进行设定,order 越小越优先。
验证 ZuulFilter 执行顺序
根据 pre、route、 post route 进行区分
查看源码 ZuulServlet#service 能够知道 ZuulFilter 根据类型 pre -> route -> post 优先级由高到低以此执行过滤操作。

Netflix 官方文档中,给定了一张图片同样证明 ZuulFilter 过滤链根据类型顺序执行
根据 order执行顺序
根据类型(pre、route、post) 分类执行之后,每一个分类有各自的执行顺序排列,排列参数:filterOrder 。数字越小,优先级越高。
Zuul 1.3.1 版本中 默认提供的 ZuulFilter
pre filters
| 过滤类 | 优先级(越小越优先) |
|---|---|
| ServletDetectionFilter | -3 |
| Servlet30WrapperFilter | -2 |
| FormBodyWrapperFilter | -1 |
| DebugFilter | 1 |
| PreDecorationFilter | 5 |
route filters
| 过滤类 | 优先级(越小越优先) |
|---|---|
| RibbonRoutingFilter | 10 |
| SimpleHostRoutingFilter | 100 |
| SendForwardFilter | 500 |
post filters
| 过滤类 | 优先级(越小越优先) |
|---|---|
| SendErrorFilter | 0 |
| SendResponseFilter | 1000 |
Zuul 1.3.1 版本提供的 ZuulFilter 功能简介
在 Zuul 中所有的 Filter 都需要继承 ZuulFilter。
默认 ZuulFilter 是否启用有两个判断条件
1、zuul.{FilterCLass}.{FilterType}.disable :false 开启,true 关闭 2、ZuulFilter#shouldFilter 方法,返回 true 执行,返回 false 不执行ZuulFilter 判定成功之后执行 ZuulFilter#run() 方法
pre filter
ServletDetectionFilter
ZuulFilter 执行触发条件

只要该过滤器开启,拦截所有请求进行过滤处理。
过滤执行逻辑

判定请求来源是不是 DispatcherServlet。并将判断结果作为参数存入 RequestContext中。
扩展:在 Spring MVC 中,在请求进入到 DispatcherServlet 中时,所有的标签会被打上标签:DispatcherServlet.class.getName() + “.CONTEXT”
Servlet30WrapperFilter
ZuulFilter 执行触发条件

只要该过滤器开启,拦截所有请求进行过滤处理。
过滤执行逻辑

判断请来源符合如下两种条件,将请求重新包装成 Servlet30RequestWrapper
条件1、请求本身的包装为:HttpServletRequestWrapper
条件2、请求是来自 DispatcherServlet 的请求。
判定请求是否来自 DispathcerServlet ,已经在 ServletDetectionFilter 中判定并存入请求参数中,直接调用 RequestUtils#isDispatcherServletRequest 判断 ture or false
FormBodyWrapperFilter
ZuulFilter 执行触发条件

符合如下条件的请求才会被拦截
请求中的 context-type 为 application/x-www-form-urlencoded
请求来自 DispatcherServlet 并且 context-type 为 multipart/form-data
满足以上两个条件的一个的请求都会被拦截处理
过滤执行逻辑

处理的主线逻辑:将请求再一次封装,封装为 FormBodyRequestWrapper 并对 requestContext 进行 content-type 的赋值操作。(FormBodyRequestWrapper 继承 Servlet30RequestWrapper)。
DebugFilter
ZuulFilter 执行触发条件

触发条件有两种:
全局开启调试
zuul.debug.request = true 。默认:false。对所有请求进行拦截标记。针对请求设置特定参数,单一请求调试
在 request 中设置 debug=true 参数,进行单请求设定。
过滤执行逻辑

DebugFilter 过滤器执行也很简单就是在 RequestContext 中设置 debugRouting 和 debugRequest 为 true。
开启调试之后,会在请求上下文 RequestContext 中打上标记,可以根据标记,在之后的过滤链,甚至业务中根据调试标记进行日志打印等其他调试操作
该过滤器可以实现在线上使用 debug 模式,打印相关日志信息。只需要在请求中将 debug=true 作为请求其中一个请求参数。
PreDecorationFilter
ZuulFilter 执行触发条件

过滤器的触发条件: RequestContext 中没有 FORWARD_TO_KEY 和 SERVICE_ID_KEY。
FORWARD_TO_KEY 和 SERVICE_ID_KEY 这两个值都是 PreDecorationFilter#run 执行的时候进行设置的。也就是说 被 PreDecorationFilter 处理过的请求不会被处理第二次
过滤执行逻辑

根据请求 requeset 获取 Route 信息,并将先关的参数信息,如服务id SERVICE_ID_KEY ,跳转链接 forwardURI 等存入 RequestContext 中。
post filters
RibbonRoutingFilter
ZuulFilter 执行触发条件

RibbonRoutingFilter 基于 Service id 的请求。请求上下文中需要有service id 该过滤器才会执行。
过滤执行逻辑

逻辑很清晰:通过前面一些过滤器的处理,其中 PreDecorationFilter 为请求调用设置基础的 service id ,请求路径等请求调用数据。
在此数据基础上 RibbonRoutingFilter 将这些参数信息封装为 Ribbon 请求参数,并以此作为参数发起 http 请求。
SimpleHostRoutingFilter
SimpleHostRoutingFilter 与 RibbonRoutingFilter 一样是完成 http 请求转发,不同的是 SimpleHostRoutingFilter 中的请求基于 URL,RibbonRoutingFilter 基于 Ribbon 或者 Eureka 维护的 Service id
扩展:通过在 SimpleHostRoutingFilter 实现新的 ZuulFilter ,实现自己维护服务列表。
ZuulFilter 执行触发条件

请求上下文中,请求参数带有指定请求主机。则执行该过滤链。
过滤执行逻辑

其中 SimpleHostRoutingFilter#forward 中执行逻辑:拼接 HttpClient 请求参数,发起 http 请求,获取响应数据。
SendForwardFilter
SendForwardFilter 处理需要转发的请求。
ZuulFilter 执行触发条件

需要本地跳转的请求,进行拦截处理。
执行逻辑

post filters
SendErrorFilter
处理异常的响应
ZuulFilter 执行触发条件

异常响应时触发。
执行逻辑

进行 Response 相关异常属性设置,包括 status_code,exception,message
SendResponseFilter
处理正常响应
ZuulFilter 执行触发条件

没有异常信息,并且携带相关响应数据。也就是请求响应成功的过滤处理。
过滤执行逻辑

拼接 Response ,响应数据。
总结
zuul 本质是 ZuulServlet + ZuulFilter 的结合体。
由 ZuulServlet 链接请求,然后由 一系列 ZuulFilter 组成过滤链,对请求进行处理。
Zuul 默认提供的过滤链功能如下图:

扩展:zuul 如何支持 Hystrix 功能
原理很简单:Hystrix 提供的请求熔断限流处理,所以 Hystrix 和请求的发起是紧密关联的。所以 Hystrix 相关功能必定和 请求调用关联。
zuul 请求调用有三种:
RibbonRoutingFilter ,service id 请求转发
SimpleHostRoutingFilter, url 请求转发
SendForwardFilter,forward 请求转发
其中支持 Hystrix 功能的是 RibbonRoutingFilter。
RibbonRoutingFilter 使用 service id 的请求转发,本质上是依赖于 Ribbon 功能实现客户端请求调用。
默认需要支持 hystrix 功能,zuul 需要通过 ribbon 维护服务实例,或者通过 eureka 维护服务实例。
zuul 使用 Ribbon 请求调用时,如何集成 Hystrix
根据 RibbonRoutingFilter#forward 中的
RibbonCommand command = this.ribbonCommandFactory.create(context);
进行追踪。
追踪到最终发起请求调用的是 RibbonCommand#execute
RibbonCommand 由 RibbonCommandFactory 工厂创建。
RibbonCommandFactory 工厂自动配置
其中 RibbonCommandFactory 有3个实现类:
RestClientRibbonCommandFactory
OkHttpRibbonCommandFactory
HttpClientRibbonCommandFactory
zuul 在自动配置类 ZuulProxyAutoConfiguration 中,根据相关依赖关系进行自动配置。
聚焦其中的一个自动配置 HttpClientRibbonCommandFactory

自动配置会生成 RibbonCommandFactory 工厂,并将我们定义的 FallbackProvider 作为构造参数。
知识点:自定义实现 hystrix fallback 功能,需要自定义类实现 FallbackProvider 接口。
有了 RibbonCommandFactory 工厂,之后就是从工厂中获取RibbonCommand 实例
聚焦 RibbonCommandFactory#create
以实现类 HttpClientRibbonCommandFactory 为例

创建 HttpClientRibbonCommand ,如果请求调用服务有自定义 FallbackProvider 则将其作为构造参数。
RibbonCommand#execute 发起请求
先来看看 RibbonCommand 实现类 HttpClientRibbonCommand 相关继承关系

跟从源码 能够得到 RibbonCommand#execute 最终调用 HystrixCommand#execute 方法。
到此就能知道 Zuul 如何提供 Hystrix 熔断功能。
-- END --

长按二维码 ▲
关注 [ WTF名字好难取 ] 公众号
往期回顾
Spring Cloud Feign【源码篇】Feign 如何调用Ribbon进行客户端负载均衡
Spring Cloud Feign【源码篇】Feign Hystrix Support
Spring Cloud Feign【源码篇】Feign 如何进行服务间请求调用
Spring Cloud Hystrix【应用篇】 Hystrix Dashboard





