成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Tomcat源碼分析 | 啟動過程深度解析

開發(fā) 前端
在啟動過程中,LifecycleBase 首先發(fā)出 STARTING_PREP 事件,StandardServer 額外還會發(fā)出 CONFIGURE_START_EVENT 和 STARTING 事件,通知 LifecycleListener 在啟動前進(jìn)行準(zhǔn)備工作。

前言

前文已述,Tomcat 的初始化由 Bootstrap 反射調(diào)用 Catalina 的 load 方法完成,包括解析 server.xml、實例化各組件、初始化組件等步驟。此番,我們將深入探究 Tomcat 如何啟動 Web 應(yīng)用,并解析其加載 ServletContextListener 及 Servlet 的機(jī)制。

Tomcat 啟動邏輯層層遞進(jìn),各部件協(xié)同運(yùn)作。其啟動流程自上而下,依次啟動各個組件,如圖:

圖片圖片

承接前文,我們已解析了 Catalina.load() 方法,接下來將深入探討 daemon.start() 方法的執(zhí)行過程。

Bootstrap

daemon.start()

啟動過程與初始化類似,均由 Bootstrap 反射調(diào)用 Catalina 的 start 方法。

public void start()
    throws Exception {
    if( catalinaDaemnotallow==null ) init();

    Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
    method.invoke(catalinaDaemon, (Object [])null);
}

Catalina

public void start() {

    if (getServer() == null) {
        load();
    }

    if (getServer() == null) {
        log.fatal("Cannot start server. Server instance is not configured.");
        return;
    }

    long t1 = System.nanoTime();

    // Start the new server
    try {
        //調(diào)用Server的start方法,啟動Server組件
        getServer().start();
    } catch (LifecycleException e) {
        log.fatal(sm.getString("catalina.serverStartFail"), e);
        try {
            getServer().destroy();
        } catch (LifecycleException e1) {
            log.debug("destroy() failed for failed Server ", e1);
        }
        return;
    }

    long t2 = System.nanoTime();
    if(log.isInfoEnabled()) {
        log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
    }

    // Register shutdown hook
    // 注冊勾子,用于安全關(guān)閉tomcat
    if (useShutdownHook) {
        if (shutdownHook == null) {
            shutdownHook = new CatalinaShutdownHook();
        }
        Runtime.getRuntime().addShutdownHook(shutdownHook);

        // If JULI is being used, disable JULI's shutdown hook since
        // shutdown hooks run in parallel and log messages may be lost
        // if JULI's hook completes before the CatalinaShutdownHook()
        LogManager logManager = LogManager.getLogManager();
        if (logManager instanceof ClassLoaderLogManager) {
            ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                    false);
        }
    }

    // Bootstrap中會設(shè)置await為true,其目的在于讓tomcat在shutdown端口阻塞監(jiān)聽關(guān)閉命令
    if (await) {
        await();
        stop();
    }
}

Server

在先前的 Lifecycle 文章中,我們已闡述 StandardServer 重寫了 startInternal 方法,并在此基礎(chǔ)上實現(xiàn)了自身的啟動邏輯。

StandardServer.startInternal

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();
        }
    }
}

在啟動過程中,LifecycleBase 首先發(fā)出 STARTING_PREP 事件,StandardServer 額外還會發(fā)出 CONFIGURE_START_EVENT 和 STARTING 事件,通知 LifecycleListener 在啟動前進(jìn)行準(zhǔn)備工作。例如,NamingContextListener 會處理 CONFIGURE_START_EVENT 事件,實例化 Tomcat 相關(guān)的上下文以及 ContextResource 資源。

隨后,啟動 Service 組件,這部分邏輯將在后續(xù)文章中詳細(xì)分析。最后,LifecycleBase 發(fā)出 STARTED 事件,完成啟動流程。

Service

StandardService 的 start 代碼如下所示:

  1. 啟動 Engine,Engine 的子容器也會被啟動,Web 應(yīng)用的部署將在這一步驟完成;
  2. 啟動 Executor,這是 Tomcat 使用 Lifecycle 封裝的線程池,繼承自 java.util.concurrent.Executor 以及 Tomcat 的 Lifecycle 接口;
  3. 啟動 Connector 組件,Connector 完成 Endpoint 的啟動,此時意味著 Tomcat 可以對外提供請求服務(wù)。

StandardService.startInternal

protected void startInternal() throws LifecycleException {

    setState(LifecycleState.STARTING);

    // 啟動Engine
    if (engine != null) {
        synchronized (engine) {
            engine.start();
        }
    }

    // 啟動Executor線程池
    synchronized (executors) {
        for (Executor executor: executors) {
            executor.start();
        }
    }

    // 啟動MapperListener
    mapperListener.start();

    // 啟動Connector
    synchronized (connectorsLock) {
        for (Connector connector: connectors) {
            try {
                // If it has already failed, don't try and start it
                if (connector.getState() != LifecycleState.FAILED) {
                    connector.start();
                }
            } catch (Exception e) {
                // logger......
            }
        }
    }
}

Engine

StandardEngine 的標(biāo)準(zhǔn)實現(xiàn)為 org.apache.catalina.core.StandardEngine。其構(gòu)造函數(shù)的主要職責(zé)是使用默認(rèn)的基礎(chǔ)閥門創(chuàng)建 StandardEngine 組件。

/**
 * Create a new StandardEngine component with the default basic Valve.
 */
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;
}

讓我們來深入分析 StandardEngine.startInternal 方法。

StandardEngine.startInternal

@Override
protected synchronized void startInternal() throws LifecycleException {

    // Log our server identification information
    if(log.isInfoEnabled())
        log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());

    // Standard container startup
    super.startInternal();
}

StandardEngine、StandardHost、StandardContext、StandardWrapper 這幾個容器之間存在著父子關(guān)系。一個父容器可以包含多個子容器,并且每個子容器都對應(yīng)一個父容器。Engine 是頂層父容器,它沒有父容器。各個組件的包含關(guān)系如下圖所示:

圖片圖片

默認(rèn)情況下,StandardEngine 只有一個子容器 StandardHost,一個 StandardContext 對應(yīng)一個 Web 應(yīng)用,而一個 StandardWrapper 對應(yīng)一個 Web 應(yīng)用里面的一個 Servlet。

StandardEngine、StandardHost、StandardContext、StandardWrapper 都是繼承自 ContainerBase,各個容器的啟動是由父容器調(diào)用子容器的 start 方法完成的,也就是說由 StandardEngine 啟動 StandardHost,再由 StandardHost 啟動 StandardContext,以此類推。

由于它們都繼承自 ContainerBase,當(dāng)調(diào)用 start 啟動 Container 容器時,首先會執(zhí)行 ContainerBase 的 start 方法。ContainerBase 會尋找子容器,并在線程池中啟動子容器。StandardEngine 也不例外。

ContainerBase

ContainerBase 的 startInternal 方法如下所示,主要分為以下三個步驟:

  1. 啟動子容器
  2. 啟動 Pipeline,并發(fā)出 STARTING 事件
  3. 如果 backgroundProcessorDelay 參數(shù) >= 0,則開啟 ContainerBackgroundProcessor 線程,用于調(diào)用子容器的 backgroundProcess 方法
protected synchronized void startInternal() throws LifecycleException {
    // 省略若干代碼......

    // 把子容器的啟動步驟放在線程中處理,默認(rèn)情況下線程池只有一個線程處理任務(wù)隊列
    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])));
    }

    // 阻塞當(dāng)前線程,直到子容器start完成
    boolean fail = false;
    for (Future<Void> result : results) {
        try {
            result.get();
        } catch (Exception e) {
            log.error(sm.getString("containerBase.threadedStartFailed"), e);
            fail = true;
        }
    }

    // 啟用Pipeline
    if (pipeline instanceof Lifecycle)
        ((Lifecycle) pipeline).start();
    setState(LifecycleState.STARTING);

    // 開啟ContainerBackgroundProcessor線程用于調(diào)用子容器的backgroundProcess方法,默認(rèn)情況下backgroundProcessorDelay=-1,不會啟用該線程
    threadStart();
}

ContainerBase 會將 StartChild 任務(wù)丟給線程池處理,并獲取 Future 對象。然后,它會遍歷所有 Future,并進(jìn)行阻塞式的 result.get() 操作,這將異步啟動轉(zhuǎn)換為同步啟動,只有所有子容器都啟動完成才會繼續(xù)運(yùn)行。

我們再來看看提交到線程池的 StartChild 任務(wù),它實現(xiàn)了 java.util.concurrent.Callable 接口,并在 call 方法中完成子容器的 start 動作。

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;
    }
}

啟動 Pipeline

默認(rèn)使用 StandardPipeline 實現(xiàn)類,它也是一個 Lifecycle。在容器啟動時,StandardPipeline 會遍歷 Valve 鏈表,如果 Valve 是 Lifecycle 的子類,則會調(diào)用其 start 方法啟動 Valve 組件,代碼如下:

public class StandardPipeline extends LifecycleBase
        implements Pipeline, Contained {


    protected synchronized void startInternal() throws LifecycleException {

        Valve current = first;
        if (current == null) {
            current = basic;
        }
        while (current != null) {
            if (current instanceof Lifecycle)
                ((Lifecycle) current).start();
            current = current.getNext();
        }

        setState(LifecycleState.STARTING);
    }

}

Host

在分析 Host 時,我們可以從 Host 的構(gòu)造函數(shù)入手,該方法主要負(fù)責(zé)設(shè)置基礎(chǔ)閥門。

ublic StandardHost() {
    super();
    pipeline.setBasic(new StandardHostValve());
}

StandardEngine.startInternal

protected synchronized void startInternal() throws LifecycleException {

    // errorValve默認(rèn)使用org.apache.catalina.valves.ErrorReportValve
    String errorValve = getErrorReportValveClass();
    if ((errorValve != null) && (!errorValve.equals(""))) {
        try {
            boolean found = false;

            // 如果所有的閥門中已經(jīng)存在這個實例,則不進(jìn)行處理,否則添加到  Pipeline 中
            Valve[] valves = getPipeline().getValves();
            for (Valve valve : valves) {
                if (errorValve.equals(valve.getClass().getName())) {
                    found = true;
                    break;
                }
            }

            // 如果未找到則添加到 Pipeline 中,注意是添加到 basic valve 的前面
            // 默認(rèn)情況下,first valve 是 AccessLogValve,basic 是 StandardHostValve
            if(!found) {
                Valve valve =
                    (Valve) Class.forName(errorValve).getConstructor().newInstance();
                getPipeline().addValve(valve);
            }
        } catch (Throwable t) {
            // 處理異常,省略......
        }
    }

    // 調(diào)用父類 ContainerBase,完成統(tǒng)一的啟動動作
    super.startInternal();
}

StandardHost Pipeline 包含的 Valve 組件:

  1. basic:org.apache.catalina.core.StandardHostValve
  2. first:org.apache.catalina.valves.AccessLogValve

需要注意的是,在往 Pipeline 中添加 Valve 閥門時,是添加到 first 后面,basic 前面。

Context

接下來,讓我們深入分析 Context 的實現(xiàn)類 org.apache.catalina.core.StandardContext。

首先,我們來看看構(gòu)造方法,該方法用于設(shè)置 Context.pipeline 的基礎(chǔ)閥門。

public StandardContext() {
    super();
    pipeline.setBasic(new StandardContextValve());
    broadcaster = new NotificationBroadcasterSupport();
    // Set defaults
    if (!Globals.STRICT_SERVLET_COMPLIANCE) {
        // Strict servlet compliance requires all extension mapped servlets
        // to be checked against welcome files
        resourceOnlyServlets.add("jsp");
    }
}

啟動方法與之前分析的容器啟動方法類似,這里不再贅述。

Wrapper

Wrapper 是一個 Servlet 的包裝,我們先來看看構(gòu)造方法。其主要作用是設(shè)置基礎(chǔ)閥門 StandardWrapperValve。

public StandardWrapper() {
    super();
    swValve=new StandardWrapperValve();
    pipeline.setBasic(swValve);
    broadcaster = new NotificationBroadcasterSupport();
}

這里每個容器中的 pipeline 設(shè)置的 StandardEngineValve、StandardHostValve、StandardContextValve、StandardWrapperValve 都是非常重要的,它們在 HTTP 請求處理過程中扮演著關(guān)鍵角色,我們將在后續(xù)的文章中詳細(xì)講解。

結(jié)語

至此,整個啟動過程便告一段落。整個啟動過程由父組件控制子組件的啟動,一層層往下傳遞,直到最后全部啟動完成。

責(zé)任編輯:武曉燕 來源: 碼上遇見你
相關(guān)推薦

2014-06-23 10:31:09

Android啟動過程

2019-05-27 14:43:49

Tomcat架構(gòu)部署

2020-04-20 21:30:51

Tomcat部署架構(gòu)

2011-06-28 13:27:13

ARM Linux

2012-02-20 14:47:08

JavaPlay

2012-08-16 09:07:57

Erlang

2018-03-13 13:00:03

Linux運(yùn)維啟動分析

2024-09-09 09:29:05

2011-07-28 10:34:38

Cocoa 程序 啟動

2011-09-05 17:35:18

MTK啟動過程RTOS

2009-12-03 10:00:46

Linux系統(tǒng)啟動

2014-06-19 14:30:28

Android應(yīng)用程序進(jìn)程啟動

2014-06-19 14:54:11

Android應(yīng)用程序進(jìn)程啟動

2014-06-19 14:59:40

Android應(yīng)用程序進(jìn)程啟動

2014-06-20 11:20:37

Android應(yīng)用程序進(jìn)程啟動

2021-01-07 07:33:06

Tomcat啟動工具

2021-07-02 06:34:53

Go語言sysmon

2010-05-06 14:05:15

Unix系統(tǒng)

2014-06-19 14:25:04

Android應(yīng)用程序進(jìn)程啟動

2014-06-20 11:05:56

Android應(yīng)用程序進(jìn)程啟動
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 欧美日韩高清在线一区 | 亚洲精品自在在线观看 | 国产高清在线 | 麻豆av一区二区三区久久 | 一区二区三区日韩 | 日韩一区和二区 | 国产亚洲精品成人av久久ww | 精品国产一区二区在线 | 一区二区三区国产精品 | 一二区成人影院电影网 | 欧美一区二区免费 | 视频一区 国产精品 | 国产在线二区 | 日韩在线小视频 | 国产精品亚洲欧美日韩一区在线 | 久久久精品国产 | 天天爽网站| 亚洲性视频 | 日韩视频在线一区 | 精品国产乱码久久久久久丨区2区 | 免费高清成人 | 黄色网址在线免费观看 | 一级片在线免费播放 | 成人性生交大免费 | 久综合| 亚洲不卡av在线 | 久久久久久九九九九 | 欧美精品在欧美一区二区少妇 | 精品一区二区三区四区五区 | 国产欧美日韩在线 | 精品久久久久久久 | 亚洲一区二区三区四区五区中文 | 国产精品精品视频一区二区三区 | 噜噜噜噜狠狠狠7777视频 | 国产乱码精品一区二区三区中文 | 911影院| 91在线网站 | 欧美日本韩国一区二区 | 天天看天天操 | 欧美久久综合 | 日本欧美在线观看视频 |