Tomcat源碼解析系列(五)Engine

前言
上篇文章分析了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 方法。

相關文章
相關標籤/搜索