诺诺清蔓

简单,美好,奋发

一个对技术追求的Java萌妹子


Spring Cloud Zuul遗失的世界(二)

摘要: 接着上一篇《Spring Cloud Zuul遗失的世界(一)》,ZuulController继承了ServletWrappingController,将当前应用中的ZuulServlet直接包装为一个Controller,暴露为入口访问,在本篇文章中介绍etflix-zuul-core的代码Zuul的执行的生命周期等。

一.Netflix zuul core源码分析

com.netflix.zuul.http.ZuulServlet是ZuulFilter链执行的入口,通过service方法,提取请求到RequestContext,然后通过调用ZuulRunner,依次按顺序执行每种类型的Filter,完成整个Filter的生命周期,架构图如下所示。

zuul.png

1.1 ZuulServlet代码分析

zuul-request-lifecycle

在ZuulConfiguration中点击com.netflix.zuul.http.ZuulServlet打开代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
public class ZuulServlet extends HttpServlet {
private static final long serialVersionUID = -3374242278843351500L;
private ZuulRunner zuulRunner;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
String bufferReqsStr = config.getInitParameter("buffer-requests");
boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false;
zuulRunner = new ZuulRunner(bufferReqs);
}
@Override
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
try {
//调用zuulRunner.init(servletRequest, servletResponse)进行请求上下文的传递
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
// Marks this request as having passed through the "Zuul engine", as opposed to servlets
// explicitly bound in web.xml, for which requests will not have the same data attached
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();
try {
preRoute();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
route();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
postRoute();
} catch (ZuulException e) {
error(e);
return;
}
} catch (Throwable e) {
error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
/**
* 执行 "post" ZuulFilters
*
* @throws ZuulException
*/
void postRoute() throws ZuulException {
zuulRunner.postRoute();
}
/**
* 执行 "route" filters
*
* @throws ZuulException
*/
void route() throws ZuulException {
zuulRunner.route();
}
/**
* 执行 "pre" filters
*
* @throws ZuulException
*/
void preRoute() throws ZuulException {
zuulRunner.preRoute();
}
/**
* initializes request
*
* @param servletRequest
* @param servletResponse
*/
void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
zuulRunner.init(servletRequest, servletResponse);
}
/**
* sets error context info and executes "error" filters
*
* @param e
*/
void error(ZuulException e) {
RequestContext.getCurrentContext().setThrowable(e);
zuulRunner.error();
}
}

如上所示,ZuulServlet其实就是一个Servlet,service方法包含了Zuul的整个生命周期。

1.1.1 ZuulServlet init代码拆解分析

1
2
3
4
//init() - 注册一个ZuulRunner用于调用整个filter 链
void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
zuulRunner.init(servletRequest, servletResponse);
}

1.1.2 ZuulServlet service代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 调用init将req,res置入上下文.获取并标记上下文此session已经通过进入
@Override
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
try {
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
// Marks this request as having passed through the "Zuul engine", as opposed to servlets
// explicitly bound in web.xml, for which requests will not have the same data attached
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();
try {
preRoute();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
route();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
postRoute();
} catch (ZuulException e) {
error(e);
return;
}
} catch (Throwable e) {
error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}

正常情况下,请求只经过pre -> route -> post。
两层try…catch,内层只捕获ZuulException,而其他异常由外层捕获。
内层3个try…catch语句,只有pre,route抛出ZuulException时,才会执行errror,再执行post。而当post(88行)抛出ZuulException后,只会执行error。
外层捕获其他异常(内层try语句块中抛出的非ZuulException异常以及内层catch语句中抛出的所有异常)后,将HTTP状态码设置为500,同时交给error处理。
整个流程的终点有两个:post及error;而非只有post一个。

1.2 Zuul的请求上下文 RequestContext

com.netflix.zuul.context.RequestContext继承了ConcurrentHashMap,是Zuul Filter生命周期中处理http请求上下文,是一个threadlocal的Map.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class RequestContext extends ConcurrentHashMap<String, Object> {
private static final Logger LOG = LoggerFactory.getLogger(RequestContext.class);
protected static Class<? extends RequestContext> contextClass = RequestContext.class;
private static RequestContext testContext = null;
protected static final ThreadLocal<? extends RequestContext> threadLocal = new ThreadLocal<RequestContext>() {
@Override
protected RequestContext initialValue() {
try {
return contextClass.newInstance();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
};
//其余省略
}

1.3 ContextLifecycleFilter

com.netflix.zuul.context.ContextLifecycleFilter是调用链外围finally中remove上文中threadlocal.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ContextLifecycleFilter implements Filter {
public void destroy() {}
public void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
try {
chain.doFilter(req, res);
} finally {
RequestContext.getCurrentContext().unset();
}
}
}

1.4 Zuul的运行执行器-ZuulRunner

com.netflix.zuul.ZuulRunner,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class ZuulRunner {
private boolean bufferRequests;
/**
* Creates a new <code>ZuulRunner</code> instance.
*/
public ZuulRunner() {
this.bufferRequests = true;
}
/**
*
* @param bufferRequests - whether to wrap the ServletRequest in HttpServletRequestWrapper and buffer the body.
*/
public ZuulRunner(boolean bufferRequests) {
this.bufferRequests = bufferRequests;
}
/**
* sets HttpServlet request and HttpResponse
*
* @param servletRequest
* @param servletResponse
*/
// ZuulRunner内传入的req/res就会被替换为wrapper类增强功能:
public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
RequestContext ctx = RequestContext.getCurrentContext();
if (bufferRequests) {
ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
} else {
ctx.setRequest(servletRequest);
}
ctx.setResponse(new HttpServletResponseWrapper(servletResponse));
}
}

com.netflix.zuul.filters.ZuulServletFilter跟跟ZuulServlet是同一份代码.
com.netflix.zuul.monitoring,预留了CounterFactory与TracerFactory的接口,用来扩展实现counter与timer.

最近的文章

Spring Cloud Zuul遗失的世界(三)

摘要: 接着上一篇《Spring Cloud Zuul遗失的世界(二)》,本文主要介绍Netflix Zuul core模块的Filter链的设计和Fifter Loader和Filter Manager相关代码的设计与分析。 …

Spring Cloud Zuul 继续阅读
更早的文章

Spring Cloud Zuul遗失的世界(一)

摘要: Zuul作为java网关届的鼻祖,2016年自研网关中间件的时候,对其源码看了很多次,经过两大互联网公司中间件的洗礼之后,目前轮到自己设计一个网关中间件纳管Spring Cloud。最近抽空把自己的理解,备注一下。由于Spring cloud整合Zuul的代码过多。本文主要介绍Spring Cloud对Netflix Zuul高度抽象封装整合部分。即spring-cloud-netflix-core的代码。 …

Spring Cloud Zuul 继续阅读