从 ZuulServletFilter
或 ZuulServlet
可以看出 Zuul 的整体执行流程(坐标:com.netflix.zuul:zuul-core:1.3.1
)。
ZuulServlet
Zuul 官方示例 和 Spring Cloud Zuul 都是通过 Servlet
的形式而不是 Servlet Filter
的形式进行启动的,以下也以 Servlet 的流程举例:
1 |
|
Zuul 过滤器的执行流程
FilterProcessor # runFilters
1 | public Object runFilters(String sType) throws Throwable { |
FilterProcessor # processZuulFilter
1 | public Object processZuulFilter(ZuulFilter filter) throws ZuulException { |
ZuulFilter # runFilter
1 | public ZuulFilterResult runFilter() { |
如何中断 Zuul 过滤流程
抛异常
从上面 Zuul 的执行流程可以看出,抛异常是可以终止 同一个类型 的过滤器执行的,因为在 FilterProcessor # runFilters
循环执行某个指定类型的过滤器的时候,并没有捕捉异常,这时候如果有异常,循环就直接终止了。
ctx.setSendZuulResponse(false);
但是从 Zuul Filter 的整个生命周期(ZuulServlet
)来看,在 pre
和 route
这两个阶段 抛异常, post
类型的过滤器,仍然会执行。从 ZuulFilter # runFilter
中可以看出,除了抛异常外,shouldFilter()
返回 false
过滤器也是不执行的。
查看 Spring Cloud 内置 RibbonRoutingFilter
和 SimpleHostRoutingFilter
的 shouldFilter()
方法,里面都有一段这样的逻辑 (...) && ctx.sendZuulResponse()
, 所以把 sendZuulResponse 标记为 false,就不会转发到 Origin 了。
总结
自定义的过滤器的时候,最常实现的过滤器类型是 pre
和 post
,pre
常用来用来进行权限校验等,如果校验失败,并且 post
写的不好的, 抛异常
和 设置ctx.setSendZuulResponse(false)
都是无法阻止 post
型过滤器执行的。
建议:
- 一定要注意 过滤器的执行顺序,留意是在内置过滤器之前还是之后执行
shouldFilter()
的实现,粒度一定经要细,建议 都加上(...) && ctx.sendZuulResponse()
的逻辑,同时抛出异常,终止内置过滤器 和 自定义过滤器 的执行- 如果要重定向 ,进行以下设置,这时候 内置过滤器
LocationRewriteFilter
会进行处理ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(301);
ctx.addZuulResponseHeader("Location", "xxx");
个人感觉 Netflix Zuul 其实是只是一个 框架,还不算一个真正意义上的网关,zuul-core 并没有实现具体的Filter逻辑;Spring Cloud Zuul 内置了一些的 Filter 实现,具有的基础的代理、转发、重定向 等简单的功能;但是如果要作为一个真正的网关,如统一权限校验、流量控制、缓存、监控等功能,都需要我们自己实现。