Tomcat源碼解析系列(三)Server

前言
上篇文章分析了Tomcat的 Catalina 類,這個類的主要做用就是根據 server.xml 的配置來初始化 Tomcat 運行所須要的組件,好比 Server,Service 等等,而後調用成員變臉 Server 類對象的的 init 和 start 方法,來啓動 tomcat。
一個 Server 類的實例就表明了一個 Tomcat 的容器,一個Tomcat 進程只會有一個 Server 實例。Server 是一個接口,它的實現類是 StandardServerjava


1 Server#init 方法
StandardServer 繼承自 LifecycleMBeanBase, 而 LifecycleMBeanBase 繼承自 LifecycleBase,LifecycleBase 實現了 Lifecycle 接口,Lifecycle 定義了組件生命週期的方法,其中就包含 init(), start(), stop() 等方法。
下面看 StandardServer 的 init() 方法也就是父類LifecycleBase 的 init()
1.1 LifecycleBase#init 方法apache

@Override
public final synchronized void init() throws LifecycleException {
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }

    try {
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        initInternal();
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    } catch (Throwable t) {
        handleSubClassException(t, "lifecycleBase.initFail", toString());
    }
}


/**
 * Sub-classes implement this method to perform any instance initialisation
 * required.
 *
 * @throws LifecycleException If the initialisation fails
 */
protected abstract void initInternal() throws LifecycleException;

LifecycleBase 對 init 方法作了一個抽象,使用模板方法模式,提供 initInternal() 方法給子類實現。
init 方法裏首先判斷 state 屬性是否是爲 LifecycleState.NEW,
state 是 LifecycleBase 的一個屬性,其聲明爲segmentfault

/**
 * The current state of the source component.
 */
private volatile LifecycleState state = LifecycleState.NEW;

它的做用是用來標識當前組件的狀態。
Tomcat 裏不少組件都是有狀態的,包括 Server,Container 等,其實 Tomcat 裏只要有生命週期的組件,都會繼承自 LifecycleBase。數組

判斷完以後分別在 initInternal() 方法調用先後,調用 setStateInternal 方法將 state 的值設置成 INITIALIZING 和 INITIALIZED。tomcat

private synchronized void setStateInternal(LifecycleState state, Object data, boolean check)
        throws LifecycleException {

    if (log.isDebugEnabled()) {
        log.debug(sm.getString("lifecycleBase.setState", this, state));
    }

    if (check) {
        // Must have been triggered by one of the abstract methods (assume
        // code in this class is correct)
        // null is never a valid state
        if (state == null) {
            invalidTransition("null");
            // Unreachable code - here to stop eclipse complaining about
            // a possible NPE further down the method
            return;
        }

        // Any method can transition to failed
        // startInternal() permits STARTING_PREP to STARTING
        // stopInternal() permits STOPPING_PREP to STOPPING and FAILED to
        // STOPPING
        if (!(state == LifecycleState.FAILED ||
                (this.state == LifecycleState.STARTING_PREP &&
                        state == LifecycleState.STARTING) ||
                (this.state == LifecycleState.STOPPING_PREP &&
                        state == LifecycleState.STOPPING) ||
                (this.state == LifecycleState.FAILED &&
                        state == LifecycleState.STOPPING))) {
            // No other transition permitted
            invalidTransition(state.name());
        }
    }

    this.state = state;
    String lifecycleEvent = state.getLifecycleEvent();
    if (lifecycleEvent != null) {
        fireLifecycleEvent(lifecycleEvent, data);
    }
}

設置完 state 的狀態以後,就觸發該狀態的事件了,通知事件監聽器安全

/**
 * The list of registered LifecycleListeners for event notifications.
 */
private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();


protected void fireLifecycleEvent(String type, Object data) {
    LifecycleEvent event = new LifecycleEvent(this, type, data);
    for (LifecycleListener listener : lifecycleListeners) {
        listener.lifecycleEvent(event);
    }
}

這裏的 LifecycleListener 對象是在 Catalina 對象解析 server.xml 文件時就已經建立好並加到 lifecycleListeners 裏的。這個不是特別重要就不細講了。
1.2 LifecycleMBeanBase#initInternal 方法app

private ObjectName oname = null;
protected MBeanServer mserver = null;

protected void initInternal() throws LifecycleException {

    // If oname is not null then registration has already happened via
    // preRegister().
    if (oname == null) {
        mserver = Registry.getRegistry(null, null).getMBeanServer();

        oname = register(this, getObjectNameKeyProperties());
    }
}

LifecycleMBeanBase 的 initInternal 方法也很簡單,就是初始化 ObjectName 類型的 oname 屬性,和 MBeanServer 的 mserver 屬性你,並註冊 oname 到 mserver,這兩操做是基於 Tomcat 的安全性和隔離性的。有興趣的話能夠自行去了解一下 java 的 MBeanServer 相關知識,這裏就不細講了。接着往下看 StandardServer 的 initInternal 方法eclipse

1.3 StandardServer#initInternal 方法ide

/**
 * Invoke a pre-startup initialization. This is used to allow connectors
 * to bind to restricted ports under Unix operating environments.
 */
@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();

    // Initialize utility executor
    reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
    register(utilityExecutor, "type=UtilityExecutor");

    // Register global String cache
    // Note although the cache is global, if there are multiple Servers
    // present in the JVM (may happen when embedding) then the same cache
    // will be registered under multiple names
    onameStringCache = register(new StringCache(), "type=StringCache");

    // Register the MBeanFactory
    MBeanFactory factory = new MBeanFactory();
    factory.setContainer(this);
    onameMBeanFactory = register(factory, "type=MBeanFactory");

    // Register the naming resources
    globalNamingResources.init();

    // Populate the extension validator with JARs from common and shared
    // class loaders
    if (getCatalina() != null) {
        ClassLoader cl = getCatalina().getParentClassLoader();
        // Walk the class loader hierarchy. Stop at the system class loader.
        // This will add the shared (if present) and common class loaders
        while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
            if (cl instanceof URLClassLoader) {
                URL[] urls = ((URLClassLoader) cl).getURLs();
                for (URL url : urls) {
                    if (url.getProtocol().equals("file")) {
                        try {
                            File f = new File (url.toURI());
                            if (f.isFile() &&
                                    f.getName().endsWith(".jar")) {
                                ExtensionValidator.addSystemResource(f);
                            }
                        } catch (URISyntaxException e) {
                            // Ignore
                        } catch (IOException e) {
                            // Ignore
                        }
                    }
                }
            }
            cl = cl.getParent();
        }
    }
    // Initialize our defined Services
    for (int i = 0; i < services.length; i++) {
        services[i].init();
    }
}

整個方法能夠分爲三段,分別是 if 塊以前、if 塊、和 if 塊以後的 for 循環。
1.2.1 第一段 ui

第一段代碼的第一行,調用了 super.initInternal() 方法,也就是調用了 LifecycleMBeanBase#initInternal 方法。而後調用
reconfigureUtilityExecutor 方法初始化了 utilityExecutor 屬性和 utilityExecutorWrapper 屬性

/**
 * Utility executor with scheduling capabilities.
 */
private ScheduledThreadPoolExecutor utilityExecutor = null;

/**
 * Utility executor wrapper.
 */
private ScheduledExecutorService utilityExecutorWrapper = null;

其中 utilityExecutorWrapper 是對 utilityExecutor 的包裝。utilityExecutorWrapper 這個線程池是用來執行一些特殊任務的,好比狀態檢查等,關於這個線程池的用處,後面的文章會提到,這裏先略過。而後初始化 onameMBeanFactory 和 onameStringCache 兩個 ObjectName 類型的屬性,就是註冊 StringCache 對象和 MBeanFactory 對象到 MBeanServer 。
最後一行就是調用 globalNamingResources 對象的 init 方法,globalNamingResources 對象是在 StandardServer 類的構造方法裏初始化的。

/**
 * Global naming resources.
 */
private NamingResourcesImpl globalNamingResources = null;

globalNamingResources 是 tomcat 支持的一個比較重要的特性,本篇文章暫時不進行深刻的解析。

1.2.2 第二段

第二段的 if 代碼塊,是將 catalina 的 parentClassLoader (這個屬性在 Bootstrap#init 方法裏經過反射調用 Catalina 的 setParentClassLoader 將 sharedClassLoader 傳進去,也就說,這個 parentClassLoader 就是 sharedClassLoader 指向的對象,也就是一個 URLClassLoader 對象)裏能加載的 jar 文件裏的 manifest ,都添加到一個集合裏(ExtensionValidator 的一個 ArrayList 裏)

1.2.3 第三段

第三段代碼是循環調用 Server 類的屬性 services 數組的元的 init 方法

/**
 * The set of Services associated with this Server.
 */
private Service services[] = new Service[0];

Server 裏的 service 是在 server.xml 裏定義的,在 Catalina 解析 server.xml 的時候初始化,並注入到 Server 對象裏。

顯然 StandardServer#initInternal() 方法最重要的就是第三段,調用 Service#init 方法。Service 是 Tomcat 的核心組件之一。

2 Server#start 方法
start 方法跟 init 方法相似,分爲 LifecycleBase#start 和 StandardServer#startInternal 方法。
2.1 LifecycleBase#start 方法

public final synchronized void start() throws LifecycleException {

    if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
            LifecycleState.STARTED.equals(state)) {

        if (log.isDebugEnabled()) {
            Exception e = new LifecycleException();
            log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
        } else if (log.isInfoEnabled()) {
            log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
        }

        return;
    }

    if (state.equals(LifecycleState.NEW)) {
        init();
    } else if (state.equals(LifecycleState.FAILED)) {
        stop();
    } else if (!state.equals(LifecycleState.INITIALIZED) &&
            !state.equals(LifecycleState.STOPPED)) {
        invalidTransition(Lifecycle.BEFORE_START_EVENT);
    }

    try {
        setStateInternal(LifecycleState.STARTING_PREP, null, false);
        startInternal();
        if (state.equals(LifecycleState.FAILED)) {
            // This is a 'controlled' failure. The component put itself into the
            // FAILED state so call stop() to complete the clean-up.
            stop();
        } else if (!state.equals(LifecycleState.STARTING)) {
            // Shouldn't be necessary but acts as a check that sub-classes are
            // doing what they are supposed to.
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        } else {
            setStateInternal(LifecycleState.STARTED, null, false);
        }
    } catch (Throwable t) {
        // This is an 'uncontrolled' failure so put the component into the
        // FAILED state and throw an exception.
        handleSubClassException(t, "lifecycleBase.startFail", toString());
    }
}

start 方法邏輯很簡單,先作一些 state 狀態校驗,確保是能夠當前組價出在可啓動的狀態。而後就調用 startInternal 方法,啓動完成以後就把 state 設置成 LifecycleState.STARTED 狀態。

2.2 StandardServer#startInternal 方法

/**
 * Start nested components ({@link Service}s) 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 void startInternal() throws LifecycleException {

    fireLifecycleEvent(CONFIGURE_START_EVENT, null);
    setState(LifecycleState.STARTING);

    globalNamingResources.start();

    // Start our defined Services
    synchronized (servicesLock) {
        for (int i = 0; i < services.length; i++) {
            services[i].start();
        }
    }

    if (periodicEventDelay > 0) {
        monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
                new Runnable() {
                    @Override
                    public void run() {
                        startPeriodicLifecycleEvent();
                    }
                }, 0, 60, TimeUnit.SECONDS);
    }
}
    
protected void startPeriodicLifecycleEvent() {
    if (periodicLifecycleEventFuture == null || (periodicLifecycleEventFuture != null && periodicLifecycleEventFuture.isDone())) {
        if (periodicLifecycleEventFuture != null && periodicLifecycleEventFuture.isDone()) {
            // There was an error executing the scheduled task, get it and log it
            try {
                periodicLifecycleEventFuture.get();
            } catch (InterruptedException | ExecutionException e) {
                log.error(sm.getString("standardServer.periodicEventError"), e);
            }
        }
        periodicLifecycleEventFuture = getUtilityExecutor().scheduleAtFixedRate(
                new Runnable() {
                    @Override
                    public void run() {
                        fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
                    }
                }, periodicEventDelay, periodicEventDelay, TimeUnit.SECONDS);
    }
}

方法的第一行代碼先觸發 CONFIGURE_START_EVENT 事件,以便執行 StandardServer 的 LifecycleListener 監聽器,而後調用 setState 方法設置成 LifecycleBase 的 state 屬性爲 LifecycleState.STARTING。
接着就 globalNamingResources.start(),跟 initInternal 方法實際上是相似的。

再接着就調用 Service 的 start 方法來啓動 Service 組件。能夠看出,StandardServe 的 startInternal 跟 initInternal 方法相似,都是調用內部的 service 組件的相關方法。

調用完 service.init 方法後,就使用 getUtilityExecutor() 返回的線程池延遲執行startPeriodicLifecycleEvent 方法,而在 startPeriodicLifecycleEvent 方法裏,也是使用 getUtilityExecutor() 方法,按期執行 fireLifecycleEvent 方法,處理 Lifecycle.PERIODIC_EVENT 事件,若是有須要按期處理的,能夠再 Server 的 LifecycleListener 裏處理 Lifecycle.PERIODIC_EVENT 事件。

3. Lifecycle 的狀態流轉
Tomcat 給各個組件定義了一些生命週期中的狀態,在枚舉類 LifecycleState 裏

public enum LifecycleState {
    NEW(false, null),
    INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT),
    INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT),
    STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT),
    STARTING(true, Lifecycle.START_EVENT),
    STARTED(true, Lifecycle.AFTER_START_EVENT),
    STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
    STOPPING(false, Lifecycle.STOP_EVENT),
    STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
    DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
    DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
    FAILED(false, null);

    private final boolean available;
    private final String lifecycleEvent;

    private LifecycleState(boolean available, String lifecycleEvent) {
        this.available = available;
        this.lifecycleEvent = lifecycleEvent;
    }
    ……
}

這些狀態的流轉以下圖
狀態轉換圖

小結本篇文章介紹了 Server 組件,Server 組件是 tomcat 的核心組件之一,它是經過調用 init 和 start 方法來啓動 tomcat 的,而 Server 的 init 方法和 start 方法則是調用 Service 的 init 和 start 方法來啓動 Service(tomcat的另外一個核心組件)。看一看出,一個 Tomcat 進程只有一個 Server 實例,一個 Server 實例能夠包含多個 Service 對象。

相關文章
相關標籤/搜索