原创: http://www.imet.me/post/2018/07-03_zuul_1.x_source/
zuul 是什么,为什么需要 zuul,zuul 实现原理?
一、zuul overview
1.1 什么是 Zuul?
zuul 作为云服务的边界服务(Edge Service),可以用来做统一授权、压力测试、金丝雀测试(Canary Testing)、动态路由、减负载以及和 netflix 其他套件一起协作
1.2 zuul as edge service
直接用官方用图:
如上图,zuul 处于内部服务的入口,起到边界服务(Edge Service)的作用
1.3 zuul 的工作原理
先不解释看官方图:
分析一下 zuul 的思路:
- ZuulServlet: zuul 的核心其实就是 ZuulServlet, 在 NIO 之前, java web 服务都是以 Servlet 作为入口提供服务的。
- ZuulFilter Runner: 通过 Runner 来串起来不同类型的 filter, 一个 http request 经过 “pre”, “route”, “post” 这三种类型的 filter。有点 servlet filter 的味道
- Filter: 可以在 Filter 里执行业务需要的逻辑。Filter 支持 java 类型的 filter 和 groovy 类型的 filter
- Filter Loader: 如果要做到动态添加或者移除 filter 的话,groovy 脚本动态编译是个不错的选择。通过监听指定目录,然后生成 filter 类,装载到 zuul runner
- Request Context: filter 如何获取 http request/response 呢。猜对了,ThreadLocal, 通过把 HttpRequest 和 HttpResposne Wrapper 起来,放到 threadlocal 变量里。
二、zuul-core + zuul-simple-webapp 源码解读之 ZuulServlet
先跑起工程再说,跑起 zuul-simple-webapp 工程,参考官方wiki
2.1 web.xml 声明
web.xml 声明完成了以下几件事情:
-
StartServer(ServletContextListener): 是 web 容器的监听器,在容器 context 初始化时做了以下几件事情:
- mock monitor
- 初始化 FilterFileManager: 去系统变量
zuul.filter.root
目录下定时检查是否有新的脚本 - 初始化 JavaFilter
-
ZuulServlet: 是 Servlet 时代处理 http 请求的主要主体。我们源码会分析
-
ContextLifecycleFilter: 主要是及时清理 threadLocal 相关的变量: ZuulServlet 为了使 ZuulFilter 能获取Request相关的 Context, 把 requestContext 放到 threadlocal 中。
<listener>
<listener-class>com.netflix.zuul.StartServer</listener-class>
</listener>
<servlet>
<servlet-name>Zuul</servlet-name>
<servlet-class>com.netflix.zuul.http.ZuulServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Zuul</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>ContextLifecycleFilter</filter-name>
<filter-class>com.netflix.zuul.context.ContextLifecycleFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ContextLifecycleFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.2 ZuulServlet
a.) ZuulServlet
是继承 HttpServlet
的
通过实现 Servlet 的 init
和 servic
来分别完成初始化和处理 http 逻辑。
b.) init
方法中创建 ZuulRunner
对象。
- 查看 Config param 里是否包含
buffer-requests
- ZuulServlet 的实际执行都是在 ZuulRunner 去处理的
c.) service
方法干了以下几件事情
- 通过 ZuulRunner 初始化 request
.ZuulRunner.java
public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
// 主要就是把 request 和 resposne 放到 requestContext 的 ThreadLocal 变量里
RequestContext ctx = RequestContext.getCurrentContext();
if (bufferRequests) {
ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
} else {
ctx.setRequest(servletRequest);
}
ctx.setResponse(new HttpServletResponseWrapper(servletResponse));
}
- RequestContext 是以 ConcurrentHashMap 来存储 http 请求中 header,body 等各种参数的
- 同时通过 RequestContext.getCurrentContext() 来获取 threadlocal 中的 context
.RequestContext.java
public class RequestContext extends ConcurrentHashMap<String, Object> {
protected static Class<? extends RequestContext> contextClass = RequestContext.class;
private static RequestContext testContext = null;
protected