Container容器是所用servlet容器的父接口,也就是说作为一个servlet容器,首先必须要实现Container接口,每个tomcat服务器只能有唯一的根Container,Connector组件通过setContainer方法将Container容器和Connector关联起来。共有四种类型Container容器,分别对应不同概念的层次,每一层之间是父子的关系。
1、Engine:整个Catalina servlet引擎,标准实现为StandardEngine。
2、Host:表示包含一个或多个Context容器的虚拟主机,标准实现为StandardHost。
3、Context:表示一个web应用程序,一个Context可以有多个Wrapper,标准实现为StandardContext。
4、Wrapper:包装一个独立的Servlet容器,标准实现为StandardWrapper。
在第二节的分析中我们知道,server.xml文件中配置了Engine和Host。
复制代码
那么这四种容器之间是如何协同工作的呢?Connector将一个连接请求交给Container之后,这四类容器之间如何分工合作,怎么将请求交给特定的子容器进行处理,即一个请求是如何从Engine最终映射到一个具体的servlet的?先介绍一下整体运作流程,如下面的时序图所示:
从上图可以看出,每个Container容器都有对应的阀Valve,多个Valve组成了Pipeline,这就是Container的具体实现过程,也可以在server.xml文件中配置Pipeline和Valve的集合实现。管道Pipe包含了容器中要执行的任务,而每一个阀Valve表示一个具体的任务,在每个管道中,都会有一个默认的阀,可以添加任意数量的阀,可通过server.xml文件配置。对过滤器熟悉的话就会发现,管道和阀的工作机制和过滤器工作机制相似,Pipeline相当于过滤器链FilterChain,Valve相当于每一个过滤器Filter。阀可以处理传给它的request对象和response对象,处理完一个Valve后接着处理下一个Valve,最后处理的阀是基础阀。下面就追踪每一个容器的管道,解析容器处理请求的流程
首先是Engine容器,默认实现是StandardEngine,创建StandardEngine时实例化其基础阀,代码如下
public StandardEngine() { super(); //设置基础阀StandardEngineValve pipeline.setBasic(new StandardEngineValve()); /* Set the jmvRoute using the system property jvmRoute */ try { setJvmRoute(System.getProperty("jvmRoute")); } catch(Exception ex) { log.warn(sm.getString("standardEngine.jvmRouteFail")); } // By default, the engine will hold the reloading thread backgroundProcessorDelay = 10; }复制代码
继续跟踪StandardEngineValve的invoke()方法,源码为:
public final void invoke(Request request, Response response) throws IOException, ServletException { // 选出和该request相关的Host,在MappingData中保存了请求和容器(Host,Context,Wrapper)之间的映射 Host host = request.getHost(); if (host == null) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, sm.getString("standardEngine.noHost", request.getServerName())); return; } if (request.isAsyncSupported()) { request.setAsyncSupported(host.getPipeline().isAsyncSupported()); } // host.getPipeline()得到Host对应的管道Pipeline,将request和response对象交给Host的阀去处理 host.getPipeline().getFirst().invoke(request, response);复制代码
StandardEngineValve的invoke()方法是在CoyoteAdapter类中调用的,也就是Connector将请求交给Container的过程:
//connector.getService().getContainer()得到Connector关联的Container,然后将request和response对象交给Engine的管道Pineline中的阀去处理。 if (!request.isAsyncDispatching() && request.isAsync() && response.isErrorReportRequired()) { connector.getService().getContainer().getPipeline().getFirst().invoke( request, response); } if (request.isAsyncDispatching()) { connector.getService().getContainer().getPipeline().getFirst().invoke( request, response); Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); if (t != null) { asyncConImpl.setErrorState(t, true); } }复制代码
同样Host容器构造器中设置了其基础阀StandardHostValve
public StandardHost() { super(); pipeline.setBasic(new StandardHostValve()); }复制代码
同样跟踪StandardHostValve的invoke方法
public final void invoke(Request request, Response response) throws IOException, ServletException { // 该request容器关联的Context,保存在MappingData中 Context context = request.getContext(); if (context == null) { return; } //是否支持异步 if (request.isAsyncSupported()) { request.setAsyncSupported(context.getPipeline().isAsyncSupported()); } boolean asyncAtStart = request.isAsync(); try { //设置StandardHostValve的类加载器 context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER); if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) { return; } // 将request传递给Context的阀去处理,有错误的页面必须在此处处理,不会继续向下传递到Context容器中 try { if (!response.isErrorReportRequired()) { context.getPipeline().getFirst().invoke(request, response); } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); container.getLogger().error("Exception Processing " + request.getRequestURI(), t); // If a new error occurred while trying to report a previous // error allow the original error to be reported. if (!response.isErrorReportRequired()) { request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); throwable(request, response, t); } } // Now that the request/response pair is back under container // control lift the suspension so that the error handling can // complete and/or the container can flush any remaining data response.setSuspended(false); Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); // Protect against NPEs if the context was destroyed during a // long running request. if (!context.getState().isAvailable()) { return; } //设置错误页面 if (response.isErrorReportRequired()) { if (t != null) { throwable(request, response, t); } else { status(request, response); } } if (!request.isAsync() && !asyncAtStart) { context.fireRequestDestroyEvent(request.getRequest()); } } finally { // Access a session (if present) to update last accessed time, based // on a strict interpretation of the specification if (ACCESS_SESSION) { request.getSession(false); } context.unbind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER); } }复制代码
Context和Wrapper的管道和阀的实现过程与Engine和Host完全一样,不再继续分析。最后主要解析StandardHostValve的invoke()方法,看该方法如何将request交个一个servlet处理。鉴于该方法源码太长,只展示出了部分重要代码。
public final void invoke(Request request, Response response) throws IOException, ServletException { ... //获取关联的StandardWrapper StandardWrapper wrapper = (StandardWrapper) getContainer(); Servlet servlet = null; //wrapper的父容器Context Context context = (Context) wrapper.getParent(); ... // 分配一个servlet实例处理该request try { //servlet可用时,分配servlet,接下来会跟踪allocate()方法 if (!unavailable) { servlet = wrapper.allocate(); } } catch (UnavailableException e) { //分别设置了503错误和404 not found ... } } catch (ServletException e) { ... } catch (Throwable e) { ... } // 为该request设置过滤器 ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); // 过滤器作用于该request,并且此过程中调用了servlet的service()方法 try { if ((servlet != null) && (filterChain != null)) { // Swallow output if needed if (context.getSwallowOutput()) { try { SystemLogHandler.startCapture(); if (request.isAsyncDispatching()) { request.getAsyncContextInternal().doInternalDispatch(); } else { filterChain.doFilter(request.getRequest(), response.getResponse()); } } finally { String log = SystemLogHandler.stopCapture(); if (log != null && log.length() > 0) { context.getLogger().info(log); } } } else { if (request.isAsyncDispatching()) { request.getAsyncContextInternal().doInternalDispatch(); } else { filterChain.doFilter (request.getRequest(), response.getResponse()); } } } } catch (ClientAbortException e) { ... } catch (IOException e) { ... } catch (UnavailableException e) { ... } catch (ServletException e) { ... } catch (Throwable e) { ... } // 释放该request的过滤链 if (filterChain != null) { filterChain.release(); } // 回收servlet容器实例 try { if (servlet != null) { wrapper.deallocate(servlet); } } catch (Throwable e) { ... } ... }复制代码
接着跟踪Wrapper的allocate源码:该方法主要功能是分配一个初始化了的servlet实例,其service方法可以被调用。
public Servlet allocate() throws ServletException { // servlet类没有加载时剖出异常 if (unloading) { throw new ServletException(sm.getString("standardWrapper.unloading", getName())); } boolean newInstance = false; // If not SingleThreadedModel, return the same instance every time if (!singleThreadModel) { // servlet没有加载时要先载入该servlet if (instance == null || !instanceInitialized) { synchronized (this) { if (instance == null) { try { ... //加载servlet,接下来继续分析loadServlet()方法 instance = loadServlet(); newInstance = true; //类加载之前并不知道该servlet是否为singleThreadModel,在loadServlet()中会改变singleThreadModel的值,所以此处要再判断一次 if (!singleThreadModel) { countAllocated.incrementAndGet(); } } catch (ServletException e) { throw e; } catch (Throwable e) { ... } } if (!instanceInitialized) { //初始化servlet initServlet(instance); } } } //新加载的servlet实现singleThreadModel时将instance加入到instancePool中,否则直接返回instance if (singleThreadModel) { if (newInstance) { synchronized (instancePool) { instancePool.push(instance); nInstances++; } } } else { if (log.isTraceEnabled()) { log.trace("Returning non-STM instance"); } if (!newInstance) { countAllocated.incrementAndGet(); } return instance; } } //SingleThreadedModel类型的servlet时返回instancePool中的一个instance。 synchronized (instancePool) { while (countAllocated.get() >= nInstances) { // Allocate a new instance if possible, or else wait if (nInstances < maxInstances) { try { instancePool.push(loadServlet()); nInstances++; } catch (ServletException e) { throw e; } catch (Throwable e) { ... } } else { try { instancePool.wait(); } catch (InterruptedException e) { // Ignore } } } if (log.isTraceEnabled()) { log.trace(" Returning allocated STM instance"); } countAllocated.incrementAndGet(); return instancePool.pop(); } }复制代码
接下来看一下servlet的load过程
public synchronized Servlet loadServlet() throws ServletException { // 如果不是SingleThreadModel类型的servlet,并且已经存在一个instance实例时,不需要加载。 if (!singleThreadModel && (instance != null)) return instance; ... Servlet servlet; try { ... //Context容器中的instanceManager,是一个类加载器,其newInstance方法根据class路径加载servlet InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager(); try { servlet = (Servlet) instanceManager.newInstance(servletClass); } catch (ClassCastException e) { ... } catch (Throwable e) { ... } if (multipartConfigElement == null) { MultipartConfig annotation = servlet.getClass().getAnnotation(MultipartConfig.class); if (annotation != null) { multipartConfigElement = new MultipartConfigElement(annotation); } } // Special handling for ContainerServlet instances // Note: The InstanceManager checks if the application is permitted // to load ContainerServlets if (servlet instanceof ContainerServlet) { ((ContainerServlet) servlet).setWrapper(this); } classLoadTime=(int) (System.currentTimeMillis() -t1); if (servlet instanceof SingleThreadModel) { if (instancePool == null) { instancePool = new Stack<>(); } singleThreadModel = true; //此处修改了singleThreadModel值,所以allocate方法中新加载servlet类后要重新判断这个值 } initServlet(servlet); //初始化刚加载的servlet fireContainerEvent("load", this); loadTime=System.currentTimeMillis() -t1; } finally { if (swallowOutput) { String log = SystemLogHandler.stopCapture(); if (log != null && log.length() > 0) { if (getServletContext() != null) { getServletContext().log(log); } else { out.println(log); } } } } return servlet; }复制代码
通过以上分析,我们知道了一个request请求是如何从Engine容器一路流动到了具体处理容器Wrapper中的,就是通过管道和阀的工作机制实现的,每一个容器都会对应一个管道,可以向管道中添加任意数量的阀valve,但必须要有一个基础阀,上一层的容器通过调用下一次容器的管道的阀的invoke方法实现request对象的传递。