本文繼續講解TOMCAT的請求原理分析,建議朋友們閱讀本文時首先閱讀過《TOMCAT源碼分析——請求原理分析(上)》和《TOMCAT源碼分析——請求原理分析(中)》。在《TOMCAT源碼分析——請求原理分析(中)》一文我簡單講到了Pipeline,但並未徹底展開,本文將從Pipeline開始講解請求原理的剩餘內容。html
在Tomcat中管道Pipeline是一個接口,定義了使得一組閥門Valve按照順序執行的規範,Pipeline中定義的接口以下:java
Engine、Host、Context及Wrapper等容器都定義了自身的Pipeline,每一個Pipeline都包含一到多個Valve。Valve定義了各個閥門的接口規範,其類繼承體系如圖1所示。apache
圖1 Valve的類繼承體系數組
這裏對圖1中的主要部分(LifecycleMBeanBase及Contained接口在《TOMCAT源碼分析——生命週期管理》一文詳細闡述)進行介紹:瀏覽器
有了以上對Tomcat的管道設計的講述,咱們下面詳細剖析其實現。tomcat
在《TOMCAT源碼分析——請求原理分析(中)》一文中講到執行管道的代碼如代碼清單1所示。服務器
代碼清單1架構
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
代碼清單1中的getContainer方法獲取到的實際是StandardService中的StandardEngine容器,根據《TOMCAT源碼分析——生命週期管理》一文的內容,咱們知道StandardEngine繼承自ContainerBase,因此這裏的getPipeline方法實際是ContainerBase實現的,代碼以下:app
public Pipeline getPipeline() { return (this.pipeline); }
pipeline在ContainerBase實例化時生成,代碼以下:less
protected Pipeline pipeline = CatalinaFactory.getFactory().createPipeline(this);
這裏的CatalinaFactory採用單例模式實現,要獲取CatalinaFactory實例,只能經過調用getFactory方法,見代碼清單2。createPipeline方法中建立了StandardPipeline,StandardPipeline是Pipeline的標準實現。
代碼清單2
public class CatalinaFactory { private static CatalinaFactory factory = new CatalinaFactory(); public static CatalinaFactory getFactory() { return factory; } private CatalinaFactory() { // Hide the default constructor } public String getDefaultPipelineClassName() { return StandardPipeline.class.getName(); } public Pipeline createPipeline(Container container) { Pipeline pipeline = new StandardPipeline(); pipeline.setContainer(container); return pipeline; } }
代碼清單1隨後調用了StandardPipeline的getFirst方法(見代碼清單3)用來獲取管道中的第一個Valve ,因爲Tomcat並無爲StandardEngine的StandardPipeline設置first,所以將返回StandardPipeline的basic。
代碼清單3
public Valve getFirst() { if (first != null) { return first; } return basic; }
代碼清單3中的basic的類型是StandardEngineValve,那麼它是什麼時候添加到StandardEngine的StandardPipeline中的呢?還記得《TOMCAT源碼分析——SERVER.XML文件的加載與解析》一文在介紹過的ObjectCreateRule?在執行ObjectCreateRule的begin方法時,會反射調用StandardEngine的構造器生成StandardEngine的實例,StandardEngine的構造器中就會給其StandardPipeline設置basic爲StandardEngineValve,見代碼清單4。
代碼清單4
/** * Create a new StandardEngine component with the default basic Valve. */ public StandardEngine() { super(); pipeline.setBasic(new StandardEngineValve()); /* Set the jmvRoute using the system property jvmRoute */ try { setJvmRoute(System.getProperty("jvmRoute")); } catch(Exception ex) { } // By default, the engine will hold the reloading thread backgroundProcessorDelay = 10; }
代碼清單1中最後調用了StandardEngineValve的invoke方法(見代碼清單5)正式將請求交給管道處理。根據《TOMCAT源碼分析——請求原理分析(中)》一文對postParseRequest方法的介紹,request已經被映射到相對應的Context容器(好比/manager)。此處首先調用request的getHost方法(實質是經過request映射的Context容器獲取父容器獲得,見代碼清單6)獲取Host容器,而後調用Host容器的Pipeline的getFirst方法得到AccessLogValve。AccessLogValve的invoke方法(見代碼清單7),從中能夠看出調用了getNext方法獲取Host容器的Pipeline的下一個Valve,並調用其invoke方法。
代碼清單5
@Override public final void invoke(Request request, Response response) throws IOException, ServletException { // Select the Host to be used for this Request 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().getFirst().invoke(request, response); }
代碼清單6
public Host getHost() { if (getContext() == null) return null; return (Host) getContext().getParent(); //return ((Host) mappingData.host); }
代碼清單7
@Override public void invoke(Request request, Response response) throws IOException, ServletException { final String t1Name = AccessLogValve.class.getName()+".t1"; if (getState().isAvailable() && getEnabled()) { // Pass this request on to the next valve in our pipeline long t1 = System.currentTimeMillis(); boolean asyncdispatch = request.isAsyncDispatching(); if (!asyncdispatch) { request.setAttribute(t1Name, new Long(t1)); } getNext().invoke(request, response); //we're not done with the request if (request.isAsyncDispatching()) { return; } else if (asyncdispatch && request.getAttribute(t1Name)!=null) { t1 = ((Long)request.getAttribute(t1Name)).longValue(); } long t2 = System.currentTimeMillis(); long time = t2 - t1; log(request,response, time); } else getNext().invoke(request, response); }
根據以上分析,咱們看到StandardEngine容器的Pipeline中只有一個Valve(StandardEngineValve),而StandardHost容器中有三個Valve(分別是AccessLogValve、ErrorReportValve和StandardHostValve),此外StandardContext容器中有一個Valve(StandardContextValve),StandardWrapper中也只有一個Valve(StandardWrapperValve)。這些閥門Valve經過invoke方法彼此串聯起來,最終構成的執行順序十分相似於一個管道,最終造成的管道正如圖2同樣,這也許是Pipeline名字的由來。
圖2 Tomcat管道示意圖
本文以StandardEngineValve和AccessLogValve爲例講了Valve的實現,以及Pipeline是如何串聯起來的,咱們最後看看StandardWrapperValve的實現,其它Valve的實現再也不贅述。
根據對管道和閥門的分析, 咱們知道要分析StandardWrapperValve,只需直接閱讀其invoke方法便可,見代碼清單8所示。
代碼清單8
@Override public final void invoke(Request request, Response response) throws IOException, ServletException { // Initialize local variables we may need boolean unavailable = false; Throwable throwable = null; // This should be a Request attribute... long t1=System.currentTimeMillis(); requestCount++; StandardWrapper wrapper = (StandardWrapper) getContainer(); Servlet servlet = null; Context context = (Context) wrapper.getParent(); // 省略校驗及次要代碼 // Allocate a servlet instance to process this request try { if (!unavailable) { servlet = wrapper.allocate(); } } catch (UnavailableException e) { // 省略異常處理代碼 } catch (ServletException e) { // 省略異常處理代碼 } catch (Throwable e) { // 省略異常處理代碼 } // Identify if the request is Comet related now that the servlet has been allocated boolean comet = false; if (servlet instanceof CometProcessor && request.getAttribute("org.apache.tomcat.comet.support") == Boolean.TRUE) { comet = true; request.setComet(true); } // Acknowledge the request try { response.sendAcknowledgement(); } catch (IOException e) { // 省略異常處理代碼 } catch (Throwable e) { // 省略異常處理代碼 } MessageBytes requestPathMB = request.getRequestPathMB(); DispatcherType dispatcherType = DispatcherType.REQUEST; if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC; request.setAttribute (ApplicationFilterFactory.DISPATCHER_TYPE_ATTR, dispatcherType); request.setAttribute (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR, requestPathMB); // Create the filter chain for this request ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance(); ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet); // Reset comet flag value after creating the filter chain request.setComet(false); // Call the filter chain for this request // NOTE: This also calls the servlet's service() method try { String jspFile = wrapper.getJspFile(); if (jspFile != null) request.setAttribute(Globals.JSP_FILE_ATTR, jspFile); else request.removeAttribute(Globals.JSP_FILE_ATTR); if ((servlet != null) && (filterChain != null)) { // Swallow output if needed if (context.getSwallowOutput()) { try { SystemLogHandler.startCapture(); if (request.isAsyncDispatching()) { //TODO SERVLET3 - async ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch(); } else if (comet) { filterChain.doFilterEvent(request.getEvent()); request.setComet(true); } 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()) { //TODO SERVLET3 - async ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch(); } else if (comet) { request.setComet(true); filterChain.doFilterEvent(request.getEvent()); } else { filterChain.doFilter (request.getRequest(), response.getResponse()); } } } request.removeAttribute(Globals.JSP_FILE_ATTR); } catch (ClientAbortException e) { // 省略異常處理代碼 } catch (IOException e) { // 省略異常處理代碼 } catch (UnavailableException e) { // 省略異常處理代碼 } catch (ServletException e) { // 省略異常處理代碼 } catch (Throwable e) { // 省略異常處理代碼 } // Release the filter chain (if any) for this request if (filterChain != null) { if (request.isComet()) { // If this is a Comet request, then the same chain will be used for the // processing of all subsequent events. filterChain.reuse(); } else { filterChain.release(); } } // Deallocate the allocated servlet instance try { if (servlet != null) { wrapper.deallocate(servlet); } } catch (Throwable e) { // 省略異常處理代碼 } } // If this servlet has been marked permanently unavailable, // unload it and release this instance try { if ((servlet != null) && (wrapper.getAvailable() == Long.MAX_VALUE)) { wrapper.unload(); } } catch (Throwable e) { // 省略異常處理代碼 } long t2=System.currentTimeMillis(); long time=t2-t1; processingTime += time; if( time > maxTime) maxTime=time; if( time < minTime) minTime=time; }
經過閱讀代碼清單8,咱們知道StandardWrapperValve的invoke方法的執行步驟以下:
注意:若是接收請求的Servlet實現了SingleThreadModel接口,那麼singleThreadModel屬性爲true,則Tomcat的StandardWrapper中只有一個Servlet實例,不然會建立一個Servlet實例池。
建立Filter職責鏈用到createFilterChain方法,其實現見代碼清單9。
代碼清單9
public ApplicationFilterChain createFilterChain (ServletRequest request, Wrapper wrapper, Servlet servlet) { // get the dispatcher type DispatcherType dispatcher = null; if (request.getAttribute(DISPATCHER_TYPE_ATTR) != null) { dispatcher = (DispatcherType) request.getAttribute(DISPATCHER_TYPE_ATTR); } String requestPath = null; Object attribute = request.getAttribute(DISPATCHER_REQUEST_PATH_ATTR); if (attribute != null){ requestPath = attribute.toString(); } // If there is no servlet to execute, return null if (servlet == null) return (null); boolean comet = false; // Create and initialize a filter chain object ApplicationFilterChain filterChain = null; if (request instanceof Request) { Request req = (Request) request; comet = req.isComet(); if (Globals.IS_SECURITY_ENABLED) { // Security: Do not recycle filterChain = new ApplicationFilterChain(); if (comet) { req.setFilterChain(filterChain); } } else { filterChain = (ApplicationFilterChain) req.getFilterChain(); if (filterChain == null) { filterChain = new ApplicationFilterChain(); req.setFilterChain(filterChain); } } } else { // Request dispatcher in use filterChain = new ApplicationFilterChain(); } filterChain.setServlet(servlet); filterChain.setSupport (((StandardWrapper)wrapper).getInstanceSupport()); // Acquire the filter mappings for this Context StandardContext context = (StandardContext) wrapper.getParent(); FilterMap filterMaps[] = context.findFilterMaps(); // If there are no filter mappings, we are done if ((filterMaps == null) || (filterMaps.length == 0)) return (filterChain); // Acquire the information we will need to match filter mappings String servletName = wrapper.getName(); // Add the relevant path-mapped filters to this filter chain for (int i = 0; i < filterMaps.length; i++) { if (!matchDispatcher(filterMaps[i] ,dispatcher)) { continue; } if (!matchFiltersURL(filterMaps[i], requestPath)) continue; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig == null) { // FIXME - log configuration problem continue; } boolean isCometFilter = false; if (comet) { try { isCometFilter = filterConfig.getFilter() instanceof CometFilter; } catch (Exception e) { // Note: The try catch is there because getFilter has a lot of // declared exceptions. However, the filter is allocated much // earlier } if (isCometFilter) { filterChain.addFilter(filterConfig); } } else { filterChain.addFilter(filterConfig); } } // Add filters that match on servlet name second for (int i = 0; i < filterMaps.length; i++) { if (!matchDispatcher(filterMaps[i] ,dispatcher)) { continue; } if (!matchFiltersServlet(filterMaps[i], servletName)) continue; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig == null) { // FIXME - log configuration problem continue; } boolean isCometFilter = false; if (comet) { try { isCometFilter = filterConfig.getFilter() instanceof CometFilter; } catch (Exception e) { // Note: The try catch is there because getFilter has a lot of // declared exceptions. However, the filter is allocated much // earlier } if (isCometFilter) { filterChain.addFilter(filterConfig); } } else { filterChain.addFilter(filterConfig); } } // Return the completed filter chain return (filterChain); }
根據代碼清單9,咱們整理下整個建立Filter職責鏈的過程:
調用ApplicationFilterChain的doFilter方法,執行ApplicationFilterChain中維護的Filter職責鏈。Filter職責鏈是對職責鏈模式的經典應用,咱們先經過圖3來介紹其執行流程。
圖3 Tomcat的Filter職責鏈執行流程
這裏對圖3的執行過程進行介紹:
第2步對應了圖3中M.N這個標記的M部分,第3步則對應N的部分。
本文最後從源碼實現級別分析Filter職責鏈的執行過程,首先來看ApplicationFilterChain的doFilter方法,見代碼清單10。
代碼清單10
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if( Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; try { java.security.AccessController.doPrivileged( new java.security.PrivilegedExceptionAction<Void>() { public Void run() throws ServletException, IOException { internalDoFilter(req,res); return null; } } ); } catch( PrivilegedActionException pe) { Exception e = pe.getException(); if (e instanceof ServletException) throw (ServletException) e; else if (e instanceof IOException) throw (IOException) e; else if (e instanceof RuntimeException) throw (RuntimeException) e; else throw new ServletException(e.getMessage(), e); } } else { internalDoFilter(request,response); } }
從代碼清單10看到ApplicationFilterChain的doFilter方法主要調用了internalDoFilter方法(見代碼清單11)。
代碼清單11
private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // Call the next filter if there is one if (pos < n) { ApplicationFilterConfig filterConfig = filters[pos++]; Filter filter = null; try { filter = filterConfig.getFilter(); support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT, filter, request, response); if (request.isAsyncSupported() && "false".equalsIgnoreCase( filterConfig.getFilterDef().getAsyncSupported())) { request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE); } if( Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res, this}; SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal); args = null; } else { filter.doFilter(request, response, this); } support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response); } catch (IOException e) { if (filter != null) support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response, e); throw e; } catch (ServletException e) { if (filter != null) support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response, e); throw e; } catch (RuntimeException e) { if (filter != null) support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response, e); throw e; } catch (Throwable e) { if (filter != null) support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response, e); throw new ServletException (sm.getString("filterChain.filter"), e); } return; } // We fell off the end of the chain -- call the servlet instance try { if (ApplicationDispatcher.WRAP_SAME_OBJECT) { lastServicedRequest.set(request); lastServicedResponse.set(response); } support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT, servlet, request, response); if (request.isAsyncSupported() && !support.getWrapper().isAsyncSupported()) { request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE); } // Use potentially wrapped request from this point if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) { if( Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res}; SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args, principal); args = null; } else { servlet.service(request, response); } } else { servlet.service(request, response); } support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response); } catch (IOException e) { support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response, e); throw e; } catch (ServletException e) { support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response, e); throw e; } catch (RuntimeException e) { support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response, e); throw e; } catch (Throwable e) { support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response, e); throw new ServletException (sm.getString("filterChain.servlet"), e); } finally { if (ApplicationDispatcher.WRAP_SAME_OBJECT) { lastServicedRequest.set(null); lastServicedResponse.set(null); } } }
從代碼清單11,咱們能夠看到ApplicationFilterChain最後會執行Servlet的service方法,此service方法實際是全部Servlet的父類HttpServlet實現的,見代碼清單12。
代碼清單12
@Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest) req; response = (HttpServletResponse) res; } catch (ClassCastException e) { throw new ServletException("non-HTTP request or response"); } service(request, response); } }
代碼清單12中的service方法調用重載的service方法,後者經過判斷HttpServletRequest對象的HTTP Method,調用不一樣的方法,如GET、DELETE、POST等,見代碼清單13。
代碼清單13
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); if (ifModifiedSince < (lastModified / 1000 * 1000)) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { // // Note that this means NO servlet supports whatever // method was requested, anywhere on this server. // String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } }
以doGet方法爲例,見代碼清單14。
代碼清單14
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String protocol = req.getProtocol(); String msg = lStrings.getString("http.method_get_not_supported"); if (protocol.endsWith("1.1")) { resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg); } else { resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg); } }
不對啊!爲何doGet方法的實現只是返回了400和405錯誤呢?由於這是抽象類HttpServlet的默認實現,用戶必須實現自身的Servlet或者使用默認的DefaultServlet。
至此,Tomcat有關請求流程的主要內容已經講解完畢。歡迎你們提出寶貴意見!
如需轉載,請標明本文做者及出處——做者:jiaan.gja,本文原創首發:博客園,原文連接:http://www.cnblogs.com/jiaan-geng/p/4898871.html