首页 > 技术文章 > Spring生态系列文章 >

SpringCloud-源码分析 zuul (二)

更新时间:2019-01-03 | 阅读量(1,989)

> 本文作者:陈刚,叩丁狼高级讲师。原创文章,转载请注明出处。 在上一章节我们分析了Zuul中的各种filter,那这一章我们来跟踪一下zuul的执行流程。那么入口肯定是我们的 ZuulServlet ,他类似于 DispatcherServlet 在请求的最前面做分发。我们来看一下他的源码 ``` /** * 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,负责调用各种filter去执行, 在init初始化方法中获取并使用 buffer-requests 配置来创建了ZuulRunner 对象。 在service方法中在依次调用 preRoute ,route ,postRoute ,如果出现异常会调用 error , 这这些方法最终都会去触发 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 ZuulRunner instance. */ public ZuulRunner() { this.bufferRequests = true; } /** * * @param bufferRequests - whether to wrap the ServletRequest in HttpServletRequestWrapper and buffer the body. 在ZuulServlet中创建ZuulRunner的时候传入的配置参数 bufferRequests, 它决定了是否把 ServletRequest包装在HttpServletRequestWrapper中并缓冲主体。 */ public ZuulRunner(boolean bufferRequests) { this.bufferRequests = bufferRequests; } /** * sets HttpServlet request and HttpResponse 设置 请求对象和响应对象到 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 执行器调用各种 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的执行器 ,而Zuul中是通过各种filter来完成的,那么ZuulRunner就是负责去调用各种 filter 。 从他的注释上亦可得知 "This class initializes servlet requests and responses into the RequestContext and wraps the FilterProcessor calls to preRoute(), route(), postRoute(), and error() methods"大致意思是此类将servlet请求和响应初始化为RequestContext,并使用FilterProcessor执行器去调用preRoute(),route(),postRoute()和error()方法,我们可以从这几个方法中电源得到证实。 我们跟踪一下 FilterProcessor的源码看一下他是如何执行的 ``` 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 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(); } //真正执行ZuulFilter内存方法 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 , 他调用了 runFilters("pre")方法 ,"pre" 很明显代表了filter的类型为前置过滤器,所有的“pre”过滤器调用都在这里面发起, 而 runFilters 中通过 FilterLoader.getInstance().getFiltersByType(sType); 获取了所有的“pre” 过滤器,然后遍历通过调用Object result = processZuulFilter(zuulFilter); 方法执行 并拿到结果 。 而在processZuulFilter方法中通过执行 Filter 本身的 runFilter(); 方法完成调用 。 ```` public abstract class ZuulFilter implements IZuulFilter, Comparable { ...省略... public ZuulFilterResult runFilter() { ZuulFilterResult zr = new ZuulFilterResult(); if (!isFilterDisabled()) { if (shouldFilter()) { Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName()); try { //真正执行ZuulFilter内存方法 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; } ```` 这里最终调用了 IZuulFilter的run()方法 ,那其实最终就调用到了 IZuulFilter的各种实现类filter的run方法,完成业务处理 。 到这里同学们只需要结合上一章节的每种filter的逻辑就能对zuul的执行流程有一个大致的理解了。
叩丁狼学员采访 叩丁狼学员采访
叩丁狼头条 叩丁狼头条
叩丁狼在线课程 叩丁狼在线课程