前言
上篇文章分析了Service的 init 和 start 方法,在這兩個方法中關鍵的是調用 Engine 和 Connector 的 init 和 start 方法,一個 Service 裏只有一個 Engine,有多個 Connector。本篇文章分析 Engine 的啓動。Engine 的實現類是 StandardEngine,java
1 StandardEngine#init 方法 web
StandardEngine 的父類是 ContainerBase ,而 ContainerBase 的父類是 LifecycleMBeanBase。ContainerBase 和 StandardEngine 都實現了 initInternal 和 startInternal 方法。
1.1 StandardEngine#initInternal 方法 apache
@Override protected void initInternal() throws LifecycleException { // Ensure that a Realm is present before any attempt is made to start // one. This will create the default NullRealm if necessary. getRealm(); super.initInternal(); }
StandardEngine 的 initInternal 方法中,先調用了 getRealm() 方法,確保 StandardEngine 的父類 StandardEngine中的 Realm 類型的屬性不爲空。
Realm 是 Tomcat 的特性功能,跟 NamingResouce,這裏先略過。
而後調用了 super.initInternal(),也就是 ContainerBase 的 initInternal 方法segmentfault
1.2 ContainerBase#initInternal 方法 tomcat
@Override protected void initInternal() throws LifecycleException { reconfigureStartStopExecutor(getStartStopThreads()); super.initInternal(); } /** * The number of threads available to process start and stop events for any * children associated with this container. */ private int startStopThreads = 1; protected ExecutorService startStopExecutor; @Override public int getStartStopThreads() { return startStopThreads; } private void reconfigureStartStopExecutor(int threads) { if (threads == 1) { // Use a fake executor if (!(startStopExecutor instanceof InlineExecutorService)) { startStopExecutor = new InlineExecutorService(); } } else { // Delegate utility execution to the Service Server server = Container.getService(this).getServer(); server.setUtilityThreads(threads); startStopExecutor = server.getUtilityExecutor(); } }
super.initInternal() 是調用的 LifecycleMBeanBase 的 initInternal() 方法。以前的文章裏講過了,這裏就略過。
reconfigureStartStopExecutor 方法是設置一個線程池來處理子容器啓動和關閉事件,。
能夠看出,getStartStopThreads() 返回的是成員變量 startStopThreads,而 startStopThreads 默認爲 1 ,因此 reconfigureStartStopExecutor 方法會走 if 語句,而 startStopExecutor 最開始是沒有賦值的,startStopExecutor instanceof InlineExecutorService 會返回 false,所以最終會執行 startStopExecutor = new InlineExecutorService(),InlineExecutorService 只是簡單地實現了 java.util.concurrent.AbstractExecutorService 類。
最終 reconfigureStartStopExecutor 給 startStopExecutor 這個成員變量設置了,startStopExecutor。session
2 StandardEngine#start 方法 app
StandardEngine 的 start 方法跟它的 init 方法相似。
2.1 StandardEngine#startInternal 方法 異步
/** * Start this component and implement the requirements * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. * * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ @Override protected synchronized void startInternal() throws LifecycleException { // Log our server identification information if (log.isInfoEnabled()) { log.info(sm.getString("standardEngine.start", ServerInfo.getServerInfo())); } // Standard container startup super.startInternal(); }
StandardEngine#startInternal 方法只是簡單地調用了 ContainerBase 的startInternal 方法。ide
2.2 ContainerBase#startInternal 方法 ui
/** * Start this component and implement the requirements * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. * * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ @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]))); } MultiThrowable multiThrowable = null; for (Future<Void> result : results) { try { result.get(); } catch (Throwable e) { log.error(sm.getString("containerBase.threadedStartFailed"), e); if (multiThrowable == null) { multiThrowable = new MultiThrowable(); } multiThrowable.add(e); } } if (multiThrowable != null) { throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"), multiThrowable.getThrowable()); } // Start the Valves in our pipeline (including the basic), if any if (pipeline instanceof Lifecycle) { ((Lifecycle) pipeline).start(); } setState(LifecycleState.STARTING); // Start our thread if (backgroundProcessorDelay > 0) { monitorFuture = Container.getService(ContainerBase.this).getServer() .getUtilityExecutor().scheduleWithFixedDelay( new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS); } }
首先調用了 getClusterInternal() 和 getRealmInternal() 方法分別獲取了,Cluster 和 Realm 對象,而後調用這兩個對象的 start 方法啊,若是這兩個對象是繼承自 Lifecycle 的話。
Cluster 和 Realm 是 tomcat 新增的特性,這裏就先略過不講。
其次,用 findChildren() 方法獲取子容器
並將子容器的啓動封裝在一個 StartChild 對象裏,
而後將這個 StartChild 對象丟到 startStopExecutor 中,這個 startStopExecutor 就是在文中上面介紹到的,
並等待執行結果,若是執行中出現異常,則將收集到 MultiThrowable 對象了,並拋出 LifecycleException 異常。
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; } }
StartChild 類就只是簡單執行 Container 對象的 start 方法。
Engine 的子容器是 Host,而 Host 的子容器是 Context,Context 的子容器是 Wrapper。
因此 Host#start,Context#start,Wrapper#start 會被依次調用。
Host#start,Context#start,Wrapper#start 會在後面的文章中介紹,這裏先略過。
調用完子容器的 start 方法以後,就開始調用 Pipeline 的 start 方法。
Pipeline 是 Container 用來處理請求的,上篇文章提到 Container 是處理請求的,Container 處理請求其實是交給 Pipeline 處理的,Pipeline 串聯了一個或多個 Valve,用 Value 去處理請求。
Pipeline 和 Valve 是處理Http請求很是重要的組件,後面的文章會專門講解。
最後,根據 backgroundProcessorDelay 判斷是否須要在後臺處理一些任務,若是須要就使用 Server 裏的 utilityExecutorWrapper 線程池去執行 ContainerBackgroundProcessorMonitor 任務。utilityExecutorWrapper 在這篇文章中介紹過。
backgroundProcessorDelay 的默認值是 -1,可是在 StandardEngine 裏被賦值爲 10。
protected class ContainerBackgroundProcessorMonitor implements Runnable { @Override public void run() { if (getState().isAvailable()) { threadStart(); } } }
ContainerBackgroundProcessorMonitor 任務很簡單,就是執行 threadStart() 方法。
2.3 ContainerBase#threadStart() 方法
/** * Start the background thread that will periodically check for * session timeouts. */ protected void threadStart() { if (backgroundProcessorDelay > 0 && (getState().isAvailable() || LifecycleState.STARTING_PREP.equals(getState())) && (backgroundProcessorFuture == null || backgroundProcessorFuture.isDone())) { if (backgroundProcessorFuture != null && backgroundProcessorFuture.isDone()) { // There was an error executing the scheduled task, get it and log it try { backgroundProcessorFuture.get(); } catch (InterruptedException | ExecutionException e) { log.error(sm.getString("containerBase.backgroundProcess.error"), e); } } backgroundProcessorFuture = Container.getService(this).getServer().getUtilityExecutor() .scheduleWithFixedDelay(new ContainerBackgroundProcessor(), backgroundProcessorDelay, backgroundProcessorDelay, TimeUnit.SECONDS); } }
threadStart() 的重點是使用 Server 裏的 utilityExecutorWrapper 去執行 ContainerBackgroundProcessor 任務。
2.4 ContainerBackgroundProcessor的 run 方法
/** * Private runnable class to invoke the backgroundProcess method * of this container and its children after a fixed delay. */ protected class ContainerBackgroundProcessor implements Runnable { @Override public void run() { processChildren(ContainerBase.this); } 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(sm.getString("containerBase.backgroundProcess.error"), t); } finally { if (container instanceof Context) { ((Context) container).unbind(false, originalClassLoader); } } } }
ContainerBackgroundProcessor 的一個重點是調用 Container 的 backgroundProcess() 方法,而後遞歸處理調用 ContainerBackgroundProcessor#processChildren 來調用子容器的 backgroundProcess(),另外,若是 Container 是 Context 的實現來的話,還會調用 Context#bind 方法。
StandardEngine 沒有重載 ContainerBase 的 backgroundProcess() 方法,而 StandardHost、StandardContext、StandardWrapper 都沒有從新給 backgroundProcessorDelay 賦值,因此這些類的 getBackgroundProcessorDelay() 返回的值是 -1,所以都這些類的 backgroundProcess() 都將會執行。
2.5 ContainerBase#backgroundProcess() 方法
/** * Execute a periodic task, such as reloading, etc. This method will be * invoked inside the classloading context of this container. Unexpected * throwables will be caught and logged. */ @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); }
能夠看出 ContainerBase 的 backgroundProcess() 方法依次調用 Cluster、Realm、Valve 的 backgroundProcess() 方法,而後觸發一個 Lifecycle.PERIODIC_EVENT 事件。
Cluster、Realm、Valve 不是本文的重點,這裏就不細講了。
小結本文介紹了 Engine 的 initInternal 和 startInternal 方法。在這兩個方法裏 StandardEngine 主要作的事情就是調用父類 ContainerBase 的重載方法,在 ContainerBase 的 startInternal 方法裏,依次調用了 Container 裏有的 Cluster 對象、Realm對象、子Container對象、Pipeline對象的 start 方法,而且異步調用了 Container 自身的 backgroundProcess 方法,以及遞歸調用了子類的 backgroundProcess 方法。