Tomcat 7 啟動分析(四)各元件 init、start 方法呼叫

weixin_42073629發表於2020-10-26

在正常啟動 Tomcat 7 的情況下,上篇文章分析到了執行 org.apache.catalina.core.StandardServer 的 init 和 start 方法這兒,那麼就來看看這兩個方法裡面到底幹了些什麼。

但是在 StandardServer 類裡面並沒有發現這兩個方法:

由此推知這兩方法必定是在該類的父類中已實現了,在 StandardServer 類的父類 LifecycleMBeanBase 類的父類 LifecycleBase 類裡面終於找到了這兩個方法的實現,下面先來看下 init 方法:

1	    @Override
 2	    public final synchronized void init() throws LifecycleException {
 3	        if (!state.equals(LifecycleState.NEW)) {
 4	            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
 5	        }
 6	        setStateInternal(LifecycleState.INITIALIZING, null, false);
 7
 8	        try {
 9	            initInternal();
10	        } catch (Throwable t) {
11	            ExceptionUtils.handleThrowable(t);
12	            setStateInternal(LifecycleState.FAILED, null, false);
13	            throw new LifecycleException(
14	                    sm.getString("lifecycleBase.initFail",toString()), t);
15	        }
16
17	        setStateInternal(LifecycleState.INITIALIZED, null, false);
18	    }
19
20
21	    protected abstract void initInternal() throws LifecycleException;

先將干擾程式閱讀視線的 setStateInternal 方法呼叫忽略掉(下一篇文章會詳細講解該方法),發現這裡面就做了一件事情,呼叫了一下接下來定義的抽象方法 initInternal()(第 21 行)。實際上看下 LifecycleBase 的實現類就會發現,所有的元件類幾乎都繼承了 LifecycleBase 類,所以這些元件類一般只會有 initInternal 方法的定義。(這裡所說的元件類就是前面我們分析 Digester 解析時發現的 StandardServer、StandardService、StandardEngine、StandardHost、StandardContext 等類)

這裡所說的元件可以將其理解為我們最開始分析 server.xml 時 xml 檔案裡的各個節點,父子關係也即 xml 檔案裡的父子節點。瀏覽下 LifecycleBase 的子類就會發現節點的實現類都是這個類的子類(記住這點,後面會提到)。

同樣分析 start 方法:

1	    /**
 2	     * {@inheritDoc}
 3	     */
 4	    @Override
 5	    public final synchronized void start() throws LifecycleException {
 6
 7	        if (LifecycleState.STARTING_PREP.equals(state) ||
 8	                LifecycleState.STARTING.equals(state) ||
 9	                LifecycleState.STARTED.equals(state)) {
10
11	            if (log.isDebugEnabled()) {
12	                Exception e = new LifecycleException();
13	                log.debug(sm.getString("lifecycleBase.alreadyStarted",
14	                        toString()), e);
15	            } else if (log.isInfoEnabled()) {
16	                log.info(sm.getString("lifecycleBase.alreadyStarted",
17	                        toString()));
18	            }
19
20	            return;
21	        }
22
23	        if (state.equals(LifecycleState.NEW)) {
24	            init();
25	        } else if (state.equals(LifecycleState.FAILED)){
26	            stop();
27	        } else if (!state.equals(LifecycleState.INITIALIZED) &&
28	                !state.equals(LifecycleState.STOPPED)) {
29	            invalidTransition(Lifecycle.BEFORE_START_EVENT);
30	        }
31
32	        setStateInternal(LifecycleState.STARTING_PREP, null, false);
33
34	        try {
35	            startInternal();
36	        } catch (Throwable t) {
37	            ExceptionUtils.handleThrowable(t);
38	            setStateInternal(LifecycleState.FAILED, null, false);
39	            throw new LifecycleException(
40	                    sm.getString("lifecycleBase.startFail",toString()), t);
41	        }
42
43	        if (state.equals(LifecycleState.FAILED) ||
44	                state.equals(LifecycleState.MUST_STOP)) {
45	            stop();
46	        } else {
47	            // Shouldn't be necessary but acts as a check that sub-classes are
48	            // doing what they are supposed to.
49	            if (!state.equals(LifecycleState.STARTING)) {
50	                invalidTransition(Lifecycle.AFTER_START_EVENT);
51	            }
52
53	            setStateInternal(LifecycleState.STARTED, null, false);
54	        }
55	    }
56
57
58	    /**
59	     * Sub-classes must ensure that the state is changed to
60	     * {@link LifecycleState#STARTING} during the execution of this method.
61	     * Changing state will trigger the {@link Lifecycle#START_EVENT} event.
62	     *
63	     * If a component fails to start it may either throw a
64	     * {@link LifecycleException} which will cause it's parent to fail to start
65	     * or it can place itself in the error state in which case {@link #stop()}
66	     * will be called on the failed component but the parent component will
67	     * continue to start normally.
68	     *
69	     * @throws LifecycleException
70	     */
71	    protected abstract void startInternal() throws LifecycleException;

第 7 到 21 行是 start 功能的前置校驗,這裡如果發現 start 方法已經呼叫過了,將會記錄日誌並直接返回。第 23 到 30 行如果發現 start 放的需要做的前置方法沒有呼叫完,或者呼叫出錯,將會先呼叫這些前置方法。第 32 行暫時先不管,不影響程式閱讀,第 35 行是該方法的實質,將會呼叫本類中定義的抽象方法 startInternal()(第 71 行)。下面的程式碼同上述一樣,都是一些 start 方法呼叫過程中可能出現的錯誤的錯誤處理。

從以上 init 和 start 方法的定義可以看到這兩個方法最終將會呼叫子類中定義的 initInternal 和 startInternal 。

接回本文開頭,一開始在找 StandardServer 類中 init 和 start 方法的定義,看完了上面的分析發現最終會呼叫 StandardServer 類的 initInternal 和 startInternal 兩個方法。那這兩個方法裡面幹了些什麼?

initInternal 方法:

1	    /**
 2	     * Invoke a pre-startup initialization. This is used to allow connectors
 3	     * to bind to restricted ports under Unix operating environments.
 4	     */
 5	    @Override
 6	    protected void initInternal() throws LifecycleException {
 7
 8	        super.initInternal();
 9
10	        // Register global String cache
11	        // Note although the cache is global, if there are multiple Servers
12	        // present in the JVM (may happen when embedding) then the same cache
13	        // will be registered under multiple names
14	        onameStringCache = register(new StringCache(), "type=StringCache");
15
16	        // Register the MBeanFactory
17	        MBeanFactory factory = new MBeanFactory();
18	        factory.setContainer(this);
19	        onameMBeanFactory = register(factory, "type=MBeanFactory");
20
21	        // Register the naming resources
22	        globalNamingResources.init();
23
24	        // Populate the extension validator with JARs from common and shared
25	        // class loaders
26	        if (getCatalina() != null) {
27	            ClassLoader cl = getCatalina().getParentClassLoader();
28	            // Walk the class loader hierarchy. Stop at the system class loader.
29	            // This will add the shared (if present) and common class loaders
30	            while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
31	                if (cl instanceof URLClassLoader) {
32	                    URL[] urls = ((URLClassLoader) cl).getURLs();
33	                    for (URL url : urls) {
34	                        if (url.getProtocol().equals("file")) {
35	                            try {
36	                                File f = new File (url.toURI());
37	                                if (f.isFile() &&
38	                                        f.getName().endsWith(".jar")) {
39	                                    ExtensionValidator.addSystemResource(f);
40	                                }
41	                            } catch (URISyntaxException e) {
42	                                // Ignore
43	                            } catch (IOException e) {
44	                                // Ignore
45	                            }
46	                        }
47	                    }
48	                }
49	                cl = cl.getParent();
50	            }
51	        }
52	        // Initialize our defined Services
53	        for (int i = 0; i < services.length; i++) {
54	            services[i].init();
55	        }
56	    }

init 方法裡面做了好幾件事情,牽涉的話題比較多,這裡重點關注最後第 53 到 55 行的程式碼,這裡將迴圈呼叫 Server 類裡內建的 Service 陣列的 init 方法。

startInternal 方法:

1	    /**
 2	     * Start nested components ({@link Service}s) and implement the requirements
 3	     * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
 4	     *
 5	     * @exception LifecycleException if this component detects a fatal error
 6	     *  that prevents this component from being used
 7	     */
 8	    @Override
 9	    protected void startInternal() throws LifecycleException {
10
11	        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
12	        setState(LifecycleState.STARTING);
13
14	        globalNamingResources.start();
15
16	        // Start our defined Services
17	        synchronized (services) {
18	            for (int i = 0; i < services.length; i++) {
19	                services[i].start();
20	            }
21	        }
22	    }

重點關注第 17 到 21 行,同上一段所分析的程式碼類似,將迴圈呼叫 Sever 類裡內建的 Service 陣列的 start 方法。

那麼這裡提到的 Service 的物件到底是什麼?

上篇文章分析 Digester 時提到“經過對 xml 檔案的解析將會產生 org.apache.catalina.core.StandardServer、org.apache.catalina.core.StandardService、org.apache.catalina.connector.Connector、org.apache.catalina.core.StandardEngine、org.apache.catalina.core.StandardHost、org.apache.catalina.core.StandardContext 等等一系列物件,這些物件從前到後前一個包含後一個物件的引用(一對一或一對多的關係)。

所以正常情況下這裡的 Service 將會是 org.apache.catalina.core.StandardService 的物件(該程式碼見 org.apache.catalina.startup.Catalina 類的 339 到 341 行)。

所以按上面的分析,接下來將會呼叫 StandardService 類的 init 和 start 方法,實際上這個類也是 LifecycleBase 類的子類,所以最終的也會呼叫本類中的 initInternal 和 startInternal 方法。

initInternal 方法:

 1	    /**
 2	     * Invoke a pre-startup initialization. This is used to allow connectors
 3	     * to bind to restricted ports under Unix operating environments.
 4	     */
 5	    @Override
 6	    protected void initInternal() throws LifecycleException {
 7
 8	        super.initInternal();
 9
10	        if (container != null) {
11	            container.init();
12	        }
13
14	        // Initialize any Executors
15	        for (Executor executor : findExecutors()) {
16	            if (executor instanceof LifecycleMBeanBase) {
17	                ((LifecycleMBeanBase) executor).setDomain(getDomain());
18	            }
19	            executor.init();
20	        }
21
22	        // Initialize our defined Connectors
23	        synchronized (connectors) {
24	            for (Connector connector : connectors) {
25	                try {
26	                    connector.init();
27	                } catch (Exception e) {
28	                    String message = sm.getString(
29	                            "standardService.connector.initFailed", connector);
30	                    log.error(message, e);
31
32	                    if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
33	                        throw new LifecycleException(message);
34	                }
35	            }
36	        }
37	    }

這裡將會呼叫 Service 下的各類子元件中的 init 方法。

startInternal 方法:

1	    /**
 2	     * Start nested components ({@link Executor}s, {@link Connector}s and
 3	     * {@link Container}s) and implement the requirements of
 4	     * {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
 5	     *
 6	     * @exception LifecycleException if this component detects a fatal error
 7	     *  that prevents this component from being used
 8	     */
 9	    @Override
10	    protected void startInternal() throws LifecycleException {
11
12	        if(log.isInfoEnabled())
13	            log.info(sm.getString("standardService.start.name", this.name));
14	        setState(LifecycleState.STARTING);
15
16	        // Start our defined Container first
17	        if (container != null) {
18	            synchronized (container) {
19	                container.start();
20	            }
21	        }
22
23	        synchronized (executors) {
24	            for (Executor executor: executors) {
25	                executor.start();
26	            }
27	        }
28
29	        // Start our defined Connectors second
30	        synchronized (connectors) {
31	            for (Connector connector: connectors) {
32	                try {
33	                    // If it has already failed, don't try and start it
34	                    if (connector.getState() != LifecycleState.FAILED) {
35	                        connector.start();
36	                    }
37	                } catch (Exception e) {
38	                    log.error(sm.getString(
39	                            "standardService.connector.startFailed",
40	                            connector), e);
41	                }
42	            }
43	        }
44	    }

同理,將會呼叫 service 下各類子元件中的 start 方法。

StandardService 的子容器是 StandardEngine ,看下 StandardEngine 的 startInternal 方法:

1	    protected synchronized void startInternal() throws LifecycleException {
2
3	        // Log our server identification information
4	        if(log.isInfoEnabled())
5	            log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());
6
7	        // Standard container startup
8	        super.startInternal();
9	    }

這裡不同於上面兩級容器的實現,而是直接呼叫了父類的 startInternal 方法: 

 1	    protected synchronized void startInternal() throws LifecycleException {
 2
 3	        // Start our subordinate components, if any
 4	        if ((loader != null) && (loader instanceof Lifecycle))
 5	            ((Lifecycle) loader).start();
 6	        logger = null;
 7	        getLogger();
 8	        if ((manager != null) && (manager instanceof Lifecycle))
 9	            ((Lifecycle) manager).start();
10	        if ((cluster != null) && (cluster instanceof Lifecycle))
11	            ((Lifecycle) cluster).start();
12	        Realm realm = getRealmInternal();
13	        if ((realm != null) && (realm instanceof Lifecycle))
14	            ((Lifecycle) realm).start();
15	        if ((resources != null) && (resources instanceof Lifecycle))
16	            ((Lifecycle) resources).start();
17
18	        // Start our child containers, if any
19	        Container children[] = findChildren();
20	        List> results = new ArrayList>();
21	        for (int i = 0; i < children.length; i++) {
22	            results.add(startStopExecutor.submit(new StartChild(children[i])));
23	        }
24
25	        boolean fail = false;
26	        for (Future result : results) {
27	            try {
28	                result.get();
29	            } catch (Exception e) {
30	                log.error(sm.getString("containerBase.threadedStartFailed"), e);
31	                fail = true;
32	            }
33
34	        }
35	        if (fail) {
36	            throw new LifecycleException(
37	                    sm.getString("containerBase.threadedStartFailed"));
38	        }
39
40	        // Start the Valves in our pipeline (including the basic), if any
41	        if (pipeline instanceof Lifecycle)
42	            ((Lifecycle) pipeline).start();
43
44
45	        setState(LifecycleState.STARTING);
46
47	        // Start our thread
48	        threadStart();
49
50	    }

第 19 到 34 行即啟動當前容器下的子容器的程式碼,這裡採用了分執行緒分別啟動的方式。核心的呼叫子容器的 start 方法的程式碼在 StartChild 類的 call 方法中: 

 1	    private static class StartChild implements Callable {
 2
 3	        private Container child;
 4
 5	        public StartChild(Container child) {
 6	            this.child = child;
 7	        }
 8
 9	        @Override
10	        public Void call() throws LifecycleException {
11	            child.start();
12	            return null;
13	        }
14	    }

這裡使用了JDK 5 的執行執行緒的方式,不理解的話請參考相關文件說明。

StandardHost 中的 startInternal 與 StandardEngine 類似,這裡不再贅述,建議有興趣的朋友逐一分析 Review 一遍,碰到元件裡面巢狀的變數不知道具體實現類的就從上篇文章裡面提到的 createStartDigester 那邊開始找起,這裡不能直接找到的就在裡面提到的 new *RuleSet 的 addRuleInstances 方法裡面找。通過這種呼叫將會最終執行完所有在 server.xml 裡配置的節點的實現類中 initInternal 和 startInternal 方法。

最後上面提到的 org.apache.catalina.core.StandardServer、org.apache.catalina.core.StandardService、org.apache.catalina.connector.Connector、org.apache.catalina.core.StandardEngine、org.apache.catalina.core.StandardHost、org.apache.catalina.core.StandardContext 等等元件的這兩個方法都會呼叫到。

就這樣,Tomcat 7 在記憶體中為這一連串元件產生物件,建立物件呼叫關係,呼叫它們各自的初始化和啟動方法,啟動的總體過程就介紹完了,這些物件 start 之後將會響應客戶端的請求,為使用者服務了。當然,這裡還沒有涉及到對於具體的釋出到 tomcat 裡面的沒有應用的載入過程,web 應用中配置的 servlet、filter、listener 等的載入、啟動服務過程,瀏覽器發起的一個請求如何經過 Tomcat 內各元件的流轉呼叫到具體應用裡去的,這一系列問題都還沒談到。因為 Tomcat 本身龐大繁雜,需要找一個視角切入進去,為了敘述的簡單,先從整體上對 Tomcat 內包含的各元件產生機制有一個大體輪廓的瞭解,這樣為後面的介紹提供一個統一的背景。

相關文章