当Zuulservlet收到请求后, 会创建一个ZuulRunner对象,该对象中初始化了RequestContext:存储请求的ServletRequest 和 ServletResponse对象,并被当前请求链上的所有Zuulfilter共享;
ZuulRunner中还有一个 FilterProcessor,FilterProcessor作为执行所有的Zuulfilter的管理器;
FilterProcessor从filterloader 中获取zuulfilter,而zuulfilter是被filterFileManager所加载,并支持groovy热加载,采用了轮询的方式热加载;
有了这些filter之后,zuulservelet首先执行的Pre类型的过滤器,再执行route类型的过滤器, 最后执行的是post 类型的过滤器,如果在执行这些过滤器有错误的时候则会执行error类型的过滤器;
执行完这些过滤器,最终将请求的结果返回给客户端。
RequestContext就是会一直跟着整个请求周期的上下文对象,filters之间有什么信息需要传递就set一些值进去就行了。
ZuulServlet 掌控所有url的流转,我们先看它做了什么工作:
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(); } @RunWith(MockitoJUnitRunner.class) public static class UnitTest { @Mock HttpServletRequest servletRequest; @Mock HttpServletResponseWrapper servletResponse; @Mock FilterProcessor processor; @Mock PrintWriter writer; @Before public void before() { MockitoAnnotations.initMocks(this); } @Test public void testProcessZuulFilter() { ZuulServlet zuulServlet = new ZuulServlet(); zuulServlet = spy(zuulServlet); RequestContext context = spy(RequestContext.getCurrentContext()); try { FilterProcessor.setProcessor(processor); RequestContext.testSetCurrentContext(context); when(servletResponse.getWriter()).thenReturn(writer); zuulServlet.init(servletRequest, servletResponse); verify(zuulServlet, times(1)).init(servletRequest, servletResponse); assertTrue(RequestContext.getCurrentContext().getRequest() instanceof HttpServletRequestWrapper); assertTrue(RequestContext.getCurrentContext().getResponse() instanceof HttpServletResponseWrapper); zuulServlet.preRoute(); verify(processor, times(1)).preRoute(); zuulServlet.postRoute(); verify(processor, times(1)).postRoute(); // verify(context, times(1)).unset(); zuulServlet.route(); verify(processor, times(1)).route(); RequestContext.testSetCurrentContext(null); } catch (Exception e) { e.printStackTrace(); } } } }