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物件的傳遞。