本文作者:陳剛籍琳,叩丁狼高級講師先舷。原創(chuàng)文章,轉(zhuǎn)載請注明出處前方。
在上一章節(jié)我們分析了Zuul中的各種filter跟继,那這一章我們來跟蹤一下zuul的執(zhí)行流程。那么入口肯定是我們的 ZuulServlet 镣丑,他類似于 DispatcherServlet 在請求的最前面做分發(fā)。我們來看一下他的源碼
/**
* Core Zuul servlet which intializes and orchestrates zuulFilter execution
*
* @author Mikey Cohen
* Date: 12/23/11
* Time: 10:44 AM
*/
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 {
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();
}
}
/**
* executes "post" ZuulFilters
*
* @throws ZuulException
*/
void postRoute() throws ZuulException {
zuulRunner.postRoute();
}
/**
* executes "route" filters
*
* @throws ZuulException
*/
void route() throws ZuulException {
zuulRunner.route();
}
/**
* executes "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();
}
翻譯類上的注釋“Core Zuul servlet which intializes and orchestrates zuulFilter execution” 娱两, 他是最核心的servlet,負責(zé)調(diào)用各種filter去執(zhí)行莺匠,
在init初始化方法中獲取并使用 buffer-requests 配置來創(chuàng)建了ZuulRunner 對象。
在service方法中在依次調(diào)用 preRoute ,route ,postRoute ,如果出現(xiàn)異常會調(diào)用 error 十兢, 這這些方法最終都會去觸發(fā) zuulRunner中的各種路由方法趣竣。我們看一下ZuulRunner的源碼
/**
* This class initializes servlet requests and responses into the RequestContext and wraps the FilterProcessor calls
* to preRoute(), route(), postRoute(), and error() methods
*
* @author mikey@netflix.com
* @version 1.0
*/
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.
在ZuulServlet中創(chuàng)建ZuulRunner的時候傳入的配置參數(shù) bufferRequests, 它決定了是否把 ServletRequest包裝在HttpServletRequestWrapper中并緩沖主體旱物。
*/
public ZuulRunner(boolean bufferRequests) {
this.bufferRequests = bufferRequests;
}
/**
* sets HttpServlet request and HttpResponse
設(shè)置 請求對象和響應(yīng)對象到 RequestContext 中
* @param servletRequest
* @param servletResponse
*/
public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
RequestContext ctx = RequestContext.getCurrentContext();
if (bufferRequests) {
//把 ServletRequest包裝在HttpServletRequestWrapper中并緩沖主體遥缕。
ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
} else {
ctx.setRequest(servletRequest);
}
ctx.setResponse(new HttpServletResponseWrapper(servletResponse));
}
//這里通過 FilterProcessor 執(zhí)行器調(diào)用各種 ZuulFilters
/**
* executes "post" filterType ZuulFilters
*
* @throws ZuulException
*/
public void postRoute() throws ZuulException {
FilterProcessor.getInstance().postRoute();
}
/**
* executes "route" filterType ZuulFilters
*
* @throws ZuulException
*/
public void route() throws ZuulException {
FilterProcessor.getInstance().route();
}
/**
* executes "pre" filterType ZuulFilters
*
* @throws ZuulException
*/
public void preRoute() throws ZuulException {
FilterProcessor.getInstance().preRoute();
}
/**
* executes "error" filterType ZuulFilters
*/
public void error() {
FilterProcessor.getInstance().error();
}
從名字上我們能知道 ZuulRunner就是Zuul的執(zhí)行器 ,而Zuul中是通過各種filter來完成的宵呛,那么ZuulRunner就是負責(zé)去調(diào)用各種 filter 单匣。
從他的注釋上亦可得知 "This class initializes servlet requests and responses into the RequestContext and wraps the FilterProcessor calls to preRoute(), route(), postRoute(), and error() methods"大致意思是此類將servlet請求和響應(yīng)初始化為RequestContext,并使用FilterProcessor執(zhí)行器去調(diào)用preRoute()宝穗,route()户秤,postRoute()和error()方法,我們可以從這幾個方法中電源得到證實逮矛。
我們跟蹤一下 FilterProcessor的源碼看一下他是如何執(zhí)行的
public class FilterProcessor {
...省略代碼...
/**
* runs "post" filters which are called after "route" filters. ZuulExceptions from ZuulFilters are thrown.
* Any other Throwables are caught and a ZuulException is thrown out with a 500 status code
*
* @throws ZuulException
*/
public void postRoute() throws ZuulException {
try {
runFilters("post");
} catch (ZuulException e) {
throw e;
} catch (Throwable e) {
throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_POST_FILTER_" + e.getClass().getName());
}
}
/**
* runs all "error" filters. These are called only if an exception occurs. Exceptions from this are swallowed and logged so as not to bubble up.
*/
public void error() {
try {
runFilters("error");
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
/**
* Runs all "route" filters. These filters route calls to an origin.
*
* @throws ZuulException if an exception occurs.
*/
public void route() throws ZuulException {
try {
runFilters("route");
} catch (ZuulException e) {
throw e;
} catch (Throwable e) {
throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_ROUTE_FILTER_" + e.getClass().getName());
}
}
/**
* runs all "pre" filters. These filters are run before routing to the orgin.
*
* @throws ZuulException
*/
public void preRoute() throws ZuulException {
try {
runFilters("pre");
} catch (ZuulException e) {
throw e;
} catch (Throwable e) {
throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());
}
}
/**
* runs all filters of the filterType sType/ Use this method within filters to run custom filters by type
*
* @param sType the filterType.
* @return
* @throws Throwable throws up an arbitrary exception
*/
public Object runFilters(String sType) throws Throwable {
if (RequestContext.getCurrentContext().debugRouting()) {
Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}
boolean bResult = false;
List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
if (list != null) {
for (int i = 0; i < list.size(); i++) {
ZuulFilter zuulFilter = list.get(i);
Object result = processZuulFilter(zuulFilter);
if (result != null && result instanceof Boolean) {
bResult |= ((Boolean) result);
}
}
}
return bResult;
}
/**
* Processes an individual ZuulFilter. This method adds Debug information. Any uncaught Thowables are caught by this method and converted to a ZuulException with a 500 status code.
*
* @param filter
* @return the return value for that filter
* @throws ZuulException
*/
public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
boolean bDebug = ctx.debugRouting();
final String metricPrefix = "zuul.filter-";
long execTime = 0;
String filterName = "";
try {
long ltime = System.currentTimeMillis();
filterName = filter.getClass().getSimpleName();
RequestContext copy = null;
Object o = null;
Throwable t = null;
if (bDebug) {
Debug.addRoutingDebug("Filter " + filter.filterType() + " " + filter.filterOrder() + " " + filterName);
copy = ctx.copy();
}
//真正執(zhí)行ZuulFilter內(nèi)存方法
ZuulFilterResult result = filter.runFilter();
ExecutionStatus s = result.getStatus();
execTime = System.currentTimeMillis() - ltime;
switch (s) {
case FAILED:
t = result.getException();
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
break;
case SUCCESS:
o = result.getResult();
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.SUCCESS.name(), execTime);
if (bDebug) {
Debug.addRoutingDebug("Filter {" + filterName + " TYPE:" + filter.filterType() + " ORDER:" + filter.filterOrder() + "} Execution time = " + execTime + "ms");
Debug.compareContextState(filterName, copy);
}
break;
default:
break;
}
if (t != null) throw t;
usageNotifier.notify(filter, s);
return o;
} catch (Throwable e) {
if (bDebug) {
Debug.addRoutingDebug("Running Filter failed " + filterName + " type:" + filter.filterType() + " order:" + filter.filterOrder() + " " + e.getMessage());
}
usageNotifier.notify(filter, ExecutionStatus.FAILED);
if (e instanceof ZuulException) {
throw (ZuulException) e;
} else {
ZuulException ex = new ZuulException(e, "Filter threw Exception", 500, filter.filterType() + ":" + filterName);
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
throw ex;
}
}
}
...省略代碼...
我們先看 preRoute 鸡号, 他調(diào)用了 runFilters("pre")方法 ,"pre" 很明顯代表了filter的類型為前置過濾器须鼎,所有的“pre”過濾器調(diào)用都在這里面發(fā)起鲸伴, 而 runFilters 中通過 FilterLoader.getInstance().getFiltersByType(sType); 獲取了所有的“pre” 過濾器府蔗,然后遍歷通過調(diào)用Object result = processZuulFilter(zuulFilter); 方法執(zhí)行 并拿到結(jié)果 。
而在processZuulFilter方法中通過執(zhí)行 Filter 本身的 runFilter(); 方法完成調(diào)用 汞窗。
public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {
...省略...
public ZuulFilterResult runFilter() {
ZuulFilterResult zr = new ZuulFilterResult();
if (!isFilterDisabled()) {
if (shouldFilter()) {
Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
try {
//真正執(zhí)行ZuulFilter內(nèi)存方法
Object res = run();
zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
} catch (Throwable e) {
t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
zr = new ZuulFilterResult(ExecutionStatus.FAILED);
zr.setException(e);
} finally {
t.stopAndLog();
}
} else {
zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
}
}
return zr;
}
這里最終調(diào)用了 IZuulFilter的run()方法 姓赤,那其實最終就調(diào)用到了 IZuulFilter的各種實現(xiàn)類filter的run方法,完成業(yè)務(wù)處理 杉辙。
到這里同學(xué)們只需要結(jié)合上一章節(jié)的每種filter的邏輯就能對zuul的執(zhí)行流程有一個大致的理解了模捂。
想獲取更多技術(shù)干貨,請前往叩丁狼官網(wǎng):http://www.wolfcode.cn/all_article.html