ZuulServlet 继承了HttpServlet,主要的作用就是对HTTP请求进行拦截做对应的处理。直接看实现方法 service()中的实现:
@Override public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException { try { init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse); //这里从RequestContext中取出当前线程中封装好的对象然后在该对象上打上zuul处理的标记 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(); } }第一句init方法:
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)); }在这里调用了一个RequestContext类将HttpServletRequest保存进去,而 RequestContext 类本身也比较特殊:
public class RequestContext extends ConcurrentHashMap<String, Object> { rotected static final ThreadLocal<? extends RequestContext> threadLocal = new ThreadLocal<RequestContext>() { @Override protected RequestContext initialValue() { try { return contextClass.newInstance(); } catch (Throwable e) { throw new RuntimeException(e); } } }; public static RequestContext getCurrentContext() { if (testContext != null) return testContext; RequestContext context = threadLocal.get(); return context; } }它本身就是一个Map,需要注意的是,该对象的使用方式并不是直接new 一个新对象,而是调用getCurrentContext()方法,该方法中返回的是 threadLocal 封装的反射生成new RequestContext的方式来创建对象。确保每个创建的 RequestContext 只在当前线程内有效,即在当前线程内,getCurrentContext()方法取出的是同一个 RequestContext对象。
继续回到service()方法,拿到了封装好了的RequestContext方法之后,下面进入四个route中,上节已经讲过这4个route都属于Filter的生命周期,在这里完成请求的过滤,转发,后置逻辑处理。route完成之后,最后的finally方法中调用了RequestContext.getCurrentContext().unset()方法,既然使用了threadLocal,必然使用完要清除,不然很可能就内存泄漏。
小憩一会:
分析到 ZuulServlet,不知你是否发现Zuul的核心。对于Zuul实现网关的功能其实就是围绕着HttpServlet拿到ServletRequest,对请求做过滤操作,拿到ServletResponse 对返回结果做后置处理操作。HttpServlet是单实例多线程的处理模型,如果存在某一个请求比较耗时,那么该线程就会一直阻塞直到处理完成返回成功才结束。假若这样的请求很多,对Zuul所在的服务器压力还是不小。
Zuul如何处理一个请求上面已经分析得出Zuul是基于Servlet这一套逻辑来做的,往下跟就变得简单。SpringMVC是如何处理请求的呢?大家应该都比较熟悉,浏览器发出一个请求到达服务端,首先到达DispatcherServlet,Servlet容器将请求交给HandlerMapping,找到对应的Controller访问路径和处理方法对应关系,接着交由HandlerAdapter路由到真实的处理逻辑中去进行处理。
上面我贴出来 ZuulServerAutoConfiguration#ZuulHandlerMapping,定义了ZuulHandlerMapping bean对象。
public class ZuulHandlerMapping extends AbstractUrlHandlerMapping { }