tomcat源码 Container
Posted 众妙之门
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了tomcat源码 Container相关的知识,希望对你有一定的参考价值。
1.Container的有四个子容器,分别是Engine,Host,Context,Wrapper,如下:
1、Engine:整个Catalina servlet引擎,标准实现为StandardEngine。
2、Host:表示包含一个或多个Context容器的虚拟主机,标准实现为StandardHost。
3、Context:表示一个web应用程序,对应着平时开发对应的一套程序,或者一个WEB-INF目录以及下面的web.xml文件一个Context可以有多个Wrapper,标准实现为StandardContext。
4、Wrapper:包装一个独立的Servlet容器,每个Wrapper封装一个Servlet,标准实现为StandardWrapper。
2.Container一个也有四个子接口Engine,Host,Context,Wrapper和一个默认实现类ContainerBase,另外这四个子容器都是对应一个StandardXXX实现类,都继承ContainerBase,并且Container还继承LifeCycle接口,所以这四个容器也是符合Tomcat的生命周期模式,结构图如下:
每个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 { // Select the Host to be used for this Request // 选出和该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()); } // Ask this Host to process this request // host.getPipeline()得到Host对应的管道Pipeline,将request和response对象交给Host的阀去处理 host.getPipeline().getFirst().invoke(request, response); }
StandardEngineValve的invoke()方法是在CoyoteAdapter类中service方法中调用的,也就是Connector将请求交给Container的过程:
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) throws Exception { Request request = (Request) req.getNote(ADAPTER_NOTES); Response response = (Response) res.getNote(ADAPTER_NOTES); ...... // Parse and set Catalina and configuration specific // request parameters //根据request得到对应的MappingData,里面保存了解析地址对应的Engine,Host,Context,Wrapper,在后面的管道中使用 postParseSuccess = postParseRequest(req, request, res, response); if (postParseSuccess) { //check valves if we support async request.setAsyncSupported( connector.getService().getContainer().getPipeline().isAsyncSupported()); // Calling the container //得到Connector关联的Container,然后将request和response对象交给Engine的管道Pineline中的阀去处理。 connector.getService().getContainer().getPipeline().getFirst().invoke( request, response); } ...... }
这个service方法就是SocketProcessor#doRun--->Http11Processor#service--->CoyoteAdapter#service
同样Host容器构造器中设置了其基础阀StandardHostValve:
public StandardHost() { super(); pipeline.setBasic(new StandardHostValve()); }
同样跟踪StandardHostValve的invoke方法:
public final void invoke(Request request, Response response) throws IOException, ServletException { // Select the Context to be used for this Request // 该request容器关联的Context,保存在MappingData中 Context context = request.getContext(); if (context == null) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, sm.getString("standardHost.noContext")); return; } //是否支持异步 if (request.isAsyncSupported()) { request.setAsyncSupported(context.getPipeline().isAsyncSupported()); } boolean asyncAtStart = request.isAsync(); boolean asyncDispatching = request.isAsyncDispatching(); try { //设置StandardHostValve的类加载器 context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER); if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) { // Don\'t fire listeners during async processing (the listener // fired for the request that called startAsync()). // If a request init listener throws an exception, the request // is aborted. return; } // Ask this Context to process this request. Requests that are in // async mode and are not being dispatched to this resource must be // in error and have been routed here to check for application // defined error pages. try { // 将request传递给Context的阀去处理,有错误的页面必须在此处处理,不会继续向下传递到Context容器中 if (!asyncAtStart || asyncDispatching) { context.getPipeline().getFirst().invoke(request, response); } else { // Make sure this request/response is here because an error // report is required. if (!response.isErrorReportRequired()) { throw new IllegalStateException(sm.getString("standardHost.asyncStateError")); } } } 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; } // Look for (and render if found) an application level error page //设置错误页面 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完全一样,不再继续分析。最后主要解析StandardWrapperValve的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(); ...... // Allocate a servlet instance to process this request // 分配一个servlet实例处理该request try { if (!unavailable) { //servlet可用时,分配servlet,接下来会跟踪allocate()方法 servlet = wrapper.allocate(); } } catch (UnavailableException e) { //分别设置了503错误和404 not found ...... } catch (ServletException e) { ...... } catch (Throwable e) { ...... } ...... // Create the filter chain for this request // 为该request设置过滤器 ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); // Call the filter chain for this request // NOTE: This also calls the servlet\'s service() method // 过滤器作用于该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) { ...... } // Release the filter chain (if any) for this request // 释放该request的过滤链 if (filterChain != null) { filterChain.release(); } // Deallocate the allocated servlet instance try { // 回收servlet容器实例 if (servlet != null) { wrapper.deallocate(servlet); } } catch (Throwable e) { ...... } ...... }
接着跟踪Wrapper的allocate源码:该方法主要功能是分配一个初始化了的servlet实例,其service方法可以被调用。
public Servlet allocate() throws ServletException { // If we are currently unloading this servlet, throw an exception // 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) { // Load and initialize our instance if necessary // servlet没有加载时要先载入该servlet if (instance == null || !instanceInitialized) { synchronized (this) { if (instance == null) { try { if (log.isDebugEnabled()) { log.debug("Allocating non-STM instance"); } // Note: We don\'t know if the Servlet implements // SingleThreadModel until we have loaded it. //加载servlet,接下来继续分析loadServlet()方法 instance = loadServlet(); newInstance = true; //类加载之前并不知道该servlet是否为singleThreadModel,在loadServlet()中会改变singleThreadModel的值,所以此处要再判断一次 if (!singleThreadModel) { // For non-STM, increment here to prevent a race // condition with unload. Bug 43683, test case // #3 countAllocated.incrementAndGet(); } } catch (ServletException e) { throw e; } catch (Throwable e) { ExceptionUtils.handleThrowable(e); throw new ServletException(sm.getString("standardWrapper.allocate"), e); } } if (!instanceInitialized) { //初始化servlet initServlet(instance); } } } //新加载的servlet实现singleThreadModel时将instance加入到instancePool中,否则直接返回instance if (singleThreadModel) { if (newInstance) { // Have to do this outside of the sync above to prevent a // possible deadlock synchronized (instancePool) { instancePool.push(instance); nInstances++; } } } else { if (log.isTraceEnabled()) { log.trace(" Returning non-STM instance"); } // For new instances, count will have been incremented at the // time of creation 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) { ExceptionUtils.handleThrowable(e); throw new ServletException(sm.getString("standardWrapper.allocate"), 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过程loadServlet:
public synchronized Servlet loadServlet() throws ServletException { // Nothing to do if we already have an instance or an instance pool // 如果不是SingleThreadModel类型的servlet,并且已经存在一个instance实例时,不需要加载。 if (!singleThreadModel && (instance != null)) return instance; ...... Servlet servlet; try { long t1=System.currentTimeMillis(); // Complain if no servlet class has been specified if (servletClass == null) { unavailable(null); throw new ServletException (sm.getString("standardWrapper.notClass", getName())); } //Context容器中的instanceManager,是一个类加载器,其newInstance方法根据class路径加载servlet InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager(); try { servlet = (Servlet) instanceManager.newInstance(servletClass); } catch (ClassCastException e) { unavailable(null); // Restore the context ClassLoader throw new ServletException (sm.getString("standardWrapper.notServlet", servletClass), e); } catch (Throwable e) { ...... } ...... if (servlet instanceof SingleThreadModel) { if (instancePool == null) { instancePool = new Stack<>(); } //此处修改了singleThreadModel值,所以allocate方法中新加载servlet类后要重新判断这个值 singleThreadModel = true; } //初始化刚加载的servlet initServlet(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对象的传递。
以上是关于tomcat源码 Container的主要内容,如果未能解决你的问题,请参考以下文章
mysql jdbc源码分析片段 和 Tomcat's JDBC Pool