經過前面幾篇文章的分析,咱們能夠看出Tomcat的核心就是Container,Engine, Host, Context和Wrapper都是Container的子類,全部的請求都是圍繞着四個類展開的,因此Container絕對是Tomcat的重中之重,本文就對Tomcat的Container進行分析。
java
public interface Container extends Lifecycle { /** * 返回Container的Pipeline,Pipeline上有多個Valve。 * 當有請求時Container會調用Pipeline上的Valve處理。 */ public Pipeline getPipeline(); /** * Container設計是父子關係的,每一個Container均可以有個Parent。 * Engine沒有Parent, Host的Parent是Engine。 */ public Container getParent(); public void setParent(Container container); public ClassLoader getParentClassLoader(); public void setParentClassLoader(ClassLoader parent); /** * Container能夠有多個Child, Engine能夠有多個Host, Wrapper則沒有Child。 */ public void addChild(Container child); public void addContainerListener(ContainerListener listener); public void addPropertyChangeListener(PropertyChangeListener listener); public Container findChild(String name); public Container[] findChildren(); public ContainerListener[] findContainerListeners(); public void removeChild(Container child); public void removeContainerListener(ContainerListener listener); public void removePropertyChangeListener(PropertyChangeListener listener); public void fireContainerEvent(String type, Object data); /** * startTopThreads線程池設計用於啓動或者中止本身多個Child。 * 並行的調用多個Child的start/stop方法。 */ public int getStartStopThreads(); public void setStartStopThreads(int startStopThreads); }
public abstract class ContainerBase extends LifecycleMBeanBase implements Container { // children記錄了Container的多個子Container protected final HashMap<String, Container> children = new HashMap<>(); // parent記錄了Container所屬的父親Container protected Container parent = null; // pipeline記錄多個Vavle protected final Pipeline pipeline = new StandardPipeline(this); // startStopExecutor用於併發執行children的start和stop private int startStopThreads = 1; protected ThreadPoolExecutor startStopExecutor; // 後臺線程用於定時執行任務 private Thread thread = null; @Override public Container getParent() { return (parent); } /** * 設置父Container,若是Contain拒絕設爲子容器會拋出異常,例如Engine */ @Override public void setParent(Container container) { Container oldParent = this.parent; this.parent = container; support.firePropertyChange("parent", oldParent, this.parent); } @Override public Pipeline getPipeline() { return (this.pipeline); } @Override public void addChild(Container child) { if (Globals.IS_SECURITY_ENABLED) { PrivilegedAction<Void> dp = new PrivilegedAddChild(child); AccessController.doPrivileged(dp); } else { addChildInternal(child); } } private void addChildInternal(Container child) { if( log.isDebugEnabled() ) log.debug("Add child " + child + " " + this); synchronized(children) { if (children.get(child.getName()) != null) throw new IllegalArgumentException("addChild: Child name '" + child.getName() + "' is not unique"); child.setParent(this); // May throw IAE children.put(child.getName(), child); } // Start child // Don't do this inside sync block - start can be a slow process and // locking the children object can cause problems elsewhere try { if ((getState().isAvailable() || LifecycleState.STARTING_PREP.equals(getState())) && startChildren) { child.start(); } } catch (LifecycleException e) { log.error("ContainerBase.addChild: start: ", e); throw new IllegalStateException("ContainerBase.addChild: start: " + e); } finally { fireContainerEvent(ADD_CHILD_EVENT, child); } } @Override public Container findChild(String name) { if (name == null) { return null; } synchronized (children) { return children.get(name); } } @Override public Container[] findChildren() { synchronized (children) { Container results[] = new Container[children.size()]; return children.values().toArray(results); } } @Override public void removeChild(Container child) { if (child == null) { return; } try { if (child.getState().isAvailable()) { child.stop(); } } catch (LifecycleException e) { log.error("ContainerBase.removeChild: stop: ", e); } try { // child.destroy() may have already been called which would have // triggered this call. If that is the case, no need to destroy the // child again. if (!LifecycleState.DESTROYING.equals(child.getState())) { child.destroy(); } } catch (LifecycleException e) { log.error("ContainerBase.removeChild: destroy: ", e); } synchronized(children) { if (children.get(child.getName()) == null) return; children.remove(child.getName()); } fireContainerEvent(REMOVE_CHILD_EVENT, child); } @Override protected void initInternal() throws LifecycleException { BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>(); startStopExecutor = new ThreadPoolExecutor( getStartStopThreadsInternal(), getStartStopThreadsInternal(), 10, TimeUnit.SECONDS, startStopQueue, new StartStopThreadFactory(getName() + "-startStop-")); startStopExecutor.allowCoreThreadTimeOut(true); super.initInternal(); } /** * Container啓動過程,經過線程池併發啓動全部Children */ @Override protected synchronized void startInternal() throws LifecycleException { // Start our subordinate components, if any logger = null; getLogger(); Cluster cluster = getClusterInternal(); if (cluster instanceof Lifecycle) { ((Lifecycle) cluster).start(); } Realm realm = getRealmInternal(); if (realm instanceof Lifecycle) { ((Lifecycle) realm).start(); } // Start our child containers, if any Container children[] = findChildren(); List<Future<Void>> results = new ArrayList<>(); for (int i = 0; i < children.length; i++) { results.add(startStopExecutor.submit(new StartChild(children[i]))); } boolean fail = false; for (Future<Void> result : results) { try { result.get(); } catch (Exception e) { log.error(sm.getString("containerBase.threadedStartFailed"), e); fail = true; } } if (fail) { throw new LifecycleException( sm.getString("containerBase.threadedStartFailed")); } // Start the Valves in our pipeline (including the basic), if any if (pipeline instanceof Lifecycle) ((Lifecycle) pipeline).start(); setState(LifecycleState.STARTING); // Start our thread threadStart(); } /** * Container中止過程,經過線程池併發中止全部Children */ @Override protected synchronized void stopInternal() throws LifecycleException { // Stop our thread threadStop(); setState(LifecycleState.STOPPING); // Stop the Valves in our pipeline (including the basic), if any if (pipeline instanceof Lifecycle && ((Lifecycle) pipeline).getState().isAvailable()) { ((Lifecycle) pipeline).stop(); } // Stop our child containers, if any Container children[] = findChildren(); List<Future<Void>> results = new ArrayList<>(); for (int i = 0; i < children.length; i++) { results.add(startStopExecutor.submit(new StopChild(children[i]))); } boolean fail = false; for (Future<Void> result : results) { try { result.get(); } catch (Exception e) { log.error(sm.getString("containerBase.threadedStopFailed"), e); fail = true; } } if (fail) { throw new LifecycleException( sm.getString("containerBase.threadedStopFailed")); } // Stop our subordinate components, if any Realm realm = getRealmInternal(); if (realm instanceof Lifecycle) { ((Lifecycle) realm).stop(); } Cluster cluster = getClusterInternal(); if (cluster instanceof Lifecycle) { ((Lifecycle) cluster).stop(); } } @Override protected void destroyInternal() throws LifecycleException { Realm realm = getRealmInternal(); if (realm instanceof Lifecycle) { ((Lifecycle) realm).destroy(); } Cluster cluster = getClusterInternal(); if (cluster instanceof Lifecycle) { ((Lifecycle) cluster).destroy(); } // Stop the Valves in our pipeline (including the basic), if any if (pipeline instanceof Lifecycle) { ((Lifecycle) pipeline).destroy(); } // Remove children now this container is being destroyed for (Container child : findChildren()) { removeChild(child); } // Required if the child is destroyed directly. if (parent != null) { parent.removeChild(this); } // If init fails, this may be null if (startStopExecutor != null) { startStopExecutor.shutdownNow(); } super.destroyInternal(); } public synchronized void addValve(Valve valve) { pipeline.addValve(valve); } /** * 定時執行上下文後臺任務 */ @Override public void backgroundProcess() { if (!getState().isAvailable()) return; Cluster cluster = getClusterInternal(); if (cluster != null) { try { cluster.backgroundProcess(); } catch (Exception e) { log.warn(sm.getString("containerBase.backgroundProcess.cluster", cluster), e); } } Realm realm = getRealmInternal(); if (realm != null) { try { realm.backgroundProcess(); } catch (Exception e) { log.warn(sm.getString("containerBase.backgroundProcess.realm", realm), e); } } Valve current = pipeline.getFirst(); while (current != null) { try { current.backgroundProcess(); } catch (Exception e) { log.warn(sm.getString("containerBase.backgroundProcess.valve", current), e); } current = current.getNext(); } fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null); } /** * ContainerBackgroundProcessor繼承Runnable用於定時執行後臺任務 */ protected class ContainerBackgroundProcessor implements Runnable { @Override public void run() { Throwable t = null; String unexpectedDeathMessage = sm.getString( "containerBase.backgroundProcess.unexpectedThreadDeath", Thread.currentThread().getName()); try { while (!threadDone) { try { Thread.sleep(backgroundProcessorDelay * 1000L); } catch (InterruptedException e) { // Ignore } if (!threadDone) { processChildren(ContainerBase.this); } } } catch (RuntimeException|Error e) { t = e; throw e; } finally { if (!threadDone) { log.error(unexpectedDeathMessage, t); } } } protected void processChildren(Container container) { ClassLoader originalClassLoader = null; try { if (container instanceof Context) { Loader loader = ((Context) container).getLoader(); // Loader will be null for FailedContext instances if (loader == null) { return; } // Ensure background processing for Contexts and Wrappers // is performed under the web app's class loader originalClassLoader = ((Context) container).bind(false, null); } container.backgroundProcess(); Container[] children = container.findChildren(); for (int i = 0; i < children.length; i++) { if (children[i].getBackgroundProcessorDelay() <= 0) { processChildren(children[i]); } } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); log.error("Exception invoking periodic operation: ", t); } finally { if (container instanceof Context) { ((Context) container).unbind(false, originalClassLoader); } } } } /** * StartChild實現Callable,加入Executor實現異步啓動Child */ private static class StartChild implements Callable<Void> { private Container child; public StartChild(Container child) { this.child = child; } @Override public Void call() throws LifecycleException { child.start(); return null; } } /** * StopChild實現Callable,加入Executor實現異步中止Child */ private static class StopChild implements Callable<Void> { private Container child; public StopChild(Container child) { this.child = child; } @Override public Void call() throws LifecycleException { if (child.getState().isAvailable()) { child.stop(); } return null; } } private static class StartStopThreadFactory implements ThreadFactory { private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; public StartStopThreadFactory(String namePrefix) { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); this.namePrefix = namePrefix; } @Override public Thread newThread(Runnable r) { Thread thread = new Thread(group, r, namePrefix + threadNumber.getAndIncrement()); thread.setDaemon(true); return thread; } } }ContainerBase實現了大部分方法,Engine, Host, Context, Wrapper經過繼承ContainerBase就能夠實現Container接口。
public void setParent(Container container) { throw new IllegalArgumentException(sm.getString("standardEngine.notParent")); }
public StandardEngine() { super(); 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; }
@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); }
StandardHost繼承了ContainerBase因此StandardHost也擁有ContainerBase全部屬性,和StandardEngine不一樣StandardHost既有父Container也有子Container。
StandardHost一樣是經過Pipeline上的Vavle處理請求,StandardHostVavle經過獲取Request的Context,將請求分發給Context的Pipeline上的Vavle處理。web
public final void invoke(Request request, Response response) throws IOException, ServletException { // Select the Context to be used for this Request 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 { context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER); if (!asyncAtStart && !context.fireRequestInitEvent(request)) { // 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 { if (!asyncAtStart || asyncDispatching) { // context處理請求 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 || !response.isErrorReportRequired())) { context.fireRequestDestroyEvent(request); } } 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); } }
StandardContext一樣是經過Pipeline上的Vavle處理請求,StandardContextVavle經過獲取Request的Context,將請求分發給Wrapper的Pipeline上的Vavle處理。session
public final void invoke(Request request, Response response) throws IOException, ServletException { // Disallow any direct access to resources under WEB-INF or META-INF MessageBytes requestPathMB = request.getRequestPathMB(); if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0)) || (requestPathMB.equalsIgnoreCase("/META-INF")) || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0)) || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // Select the Wrapper to be used for this Request Wrapper wrapper = request.getWrapper(); if (wrapper == null || wrapper.isUnavailable()) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // Acknowledge the request try { response.sendAcknowledgement(); } catch (IOException ioe) { container.getLogger().error(sm.getString( "standardContextValve.acknowledgeException"), ioe); request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } if (request.isAsyncSupported()) { request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported()); } // wrapper處理請求 wrapper.getPipeline().getFirst().invoke(request, response); }
Wrapper
Wrapper表明一個Servlet,它負責管理一個Servlet,包括的Servlet的裝載、初始化、執行以及資源回收。Wrapper是最底層的容器,它沒有子容器了,因此調用它的addChild將會報錯。StandardWrapper結構如圖所示:
StandardWrapper繼承了ContainerBase因此StandardWrapper也擁有ContainerBase全部屬性,由於StandardWrapper已是底層容器,因此設置child會拋出異常。併發
public void addChild(Container child) { throw new IllegalStateException(sm.getString("standardWrapper.notChild")); }
StandardWrapper一樣是經過Pipeline上的Vavle處理請求,StandardContexttVavle最終將請求分發給ApplicationChainFilter處理,ApplicationChainFilter最終調用Servlet的service方法處理請求。app
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.incrementAndGet(); StandardWrapper wrapper = (StandardWrapper) getContainer(); Servlet servlet = null; Context context = (Context) wrapper.getParent(); // Check for the application being marked unavailable if (!context.getState().isAvailable()) { response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString("standardContext.isUnavailable")); unavailable = true; } // Check for the servlet being marked unavailable if (!unavailable && wrapper.isUnavailable()) { container.getLogger().info(sm.getString("standardWrapper.isUnavailable", wrapper.getName())); long available = wrapper.getAvailable(); if ((available > 0L) && (available < Long.MAX_VALUE)) { response.setDateHeader("Retry-After", available); response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString("standardWrapper.isUnavailable", wrapper.getName())); } else if (available == Long.MAX_VALUE) { response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("standardWrapper.notFound", wrapper.getName())); } unavailable = true; } // Allocate a servlet instance to process this request try { if (!unavailable) { servlet = wrapper.allocate(); } } catch (UnavailableException e) { container.getLogger().error( sm.getString("standardWrapper.allocateException", wrapper.getName()), e); long available = wrapper.getAvailable(); if ((available > 0L) && (available < Long.MAX_VALUE)) { response.setDateHeader("Retry-After", available); response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString("standardWrapper.isUnavailable", wrapper.getName())); } else if (available == Long.MAX_VALUE) { response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("standardWrapper.notFound", wrapper.getName())); } } catch (ServletException e) { container.getLogger().error(sm.getString("standardWrapper.allocateException", wrapper.getName()), StandardWrapper.getRootCause(e)); throwable = e; exception(request, response, e); } catch (Throwable e) { ExceptionUtils.handleThrowable(e); container.getLogger().error(sm.getString("standardWrapper.allocateException", wrapper.getName()), e); throwable = e; exception(request, response, e); servlet = null; } MessageBytes requestPathMB = request.getRequestPathMB(); DispatcherType dispatcherType = DispatcherType.REQUEST; if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC; request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType); request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,requestPathMB); // Create the filter chain for this request ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); // Call the filter chain for this request // NOTE: This also calls the servlet's service() method 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處理請求 filterChain.doFilter (request.getRequest(), response.getResponse()); } } } } catch (ClientAbortException e) { throwable = e; exception(request, response, e); } catch (IOException e) { container.getLogger().error(sm.getString( "standardWrapper.serviceException", wrapper.getName(), context.getName()), e); throwable = e; exception(request, response, e); } catch (UnavailableException e) { container.getLogger().error(sm.getString( "standardWrapper.serviceException", wrapper.getName(), context.getName()), e); // throwable = e; // exception(request, response, e); wrapper.unavailable(e); long available = wrapper.getAvailable(); if ((available > 0L) && (available < Long.MAX_VALUE)) { response.setDateHeader("Retry-After", available); response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString("standardWrapper.isUnavailable", wrapper.getName())); } else if (available == Long.MAX_VALUE) { response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("standardWrapper.notFound", wrapper.getName())); } // Do not save exception in 'throwable', because we // do not want to do exception(request, response, e) processing } catch (ServletException e) { Throwable rootCause = StandardWrapper.getRootCause(e); if (!(rootCause instanceof ClientAbortException)) { container.getLogger().error(sm.getString( "standardWrapper.serviceExceptionRoot", wrapper.getName(), context.getName(), e.getMessage()), rootCause); } throwable = e; exception(request, response, e); } catch (Throwable e) { ExceptionUtils.handleThrowable(e); container.getLogger().error(sm.getString( "standardWrapper.serviceException", wrapper.getName(), context.getName()), e); throwable = e; exception(request, response, e); } // Release the filter chain (if any) for this request if (filterChain != null) { filterChain.release(); } // Deallocate the allocated servlet instance try { if (servlet != null) { wrapper.deallocate(servlet); } } catch (Throwable e) { ExceptionUtils.handleThrowable(e); container.getLogger().error(sm.getString("standardWrapper.deallocateException", wrapper.getName()), e); if (throwable == null) { throwable = e; exception(request, response, 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) { ExceptionUtils.handleThrowable(e); container.getLogger().error(sm.getString("standardWrapper.unloadException", wrapper.getName()), e); if (throwable == null) { throwable = e; exception(request, response, e); } } long t2=System.currentTimeMillis(); long time=t2-t1; processingTime += time; if( time > maxTime) maxTime=time; if( time < minTime) minTime=time; }