本文继续讲解TOMCAT的请求原理分析,建议朋友们阅读本文时首先阅读过《Tomcat7.0源码分析——请求原理分析(上)》和《Tomcat7.0源码分析——请求原理分析(中)》。在《Tomcat7.0源码分析——请求原理分析(中)》一文我简单讲到了Pipeline,但并未完全展开,本文将从Pipeline开始讲解请求原理的剩余内容。
在Tomcat中管道Pipeline是一个接口,定义了使得一组阀门Valve按照顺序执行的规范,Pipeline中定义的接口如下:
Engine、Host、Context及Wrapper等容器都定义了自身的Pipeline,每个Pipeline都包含一到多个Valve。Valve定义了各个阀门的接口规范,其类继承体系如图1所示。
图1 Valve的类继承体系
这里对图1中的主要部分(LifecycleMBeanBase及Contained接口在《Tomcat7.0源码分析——生命周期管理》一文已详细阐述过)进行介绍:
代码清单1
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
代码清单1中的getContainer方法获取到的实际是StandardService中的StandardEngine容器,根据《Tomcat7.0源码分析——生命周期管理》一文的内容,我们知道StandardEngine继承自ContainerBase,所以这里的getPipeline方法实际是ContainerBase实现的,代码如下:
public Pipeline getPipeline() { return (this.pipeline); }
pipeline在ContainerBase实例化时生成,代码如下:
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中的呢?还记得《Tomcat7.0源码分析——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)正式将请求交给管道处理。根据《Tomcat7.0源码分析——请求原理分析(中)》一文对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方法的执行步骤如下:
代码清单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职责链的过程:
图3 Tomcat的Filter职责链执行流程
这里对图3的执行过程进行介绍:
代码清单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有关请求流程的主要内容已经讲解完毕。欢迎大家提出宝贵意见!
后记:个人总结整理的《深入理解Spark:核心思想与源码分析》一书现在已经正式出版上市,目前京东、当当、天猫等网站均有销售,欢迎感兴趣的同学购买。
京东:http://item.jd.com/11846120.html
当当:http://product.dangdang.com/23838168.html