tomcat 各個組件初始化及啟動流程

結合源碼來探索啟動的流程

通過上一章的目錄結構和體系結構的研究,我們對tomcat有一個初步的認識
我們先找到入口函數。根據"startd"命令來推導
Bootstrap 下面的main方法就是入口函數
上面圖片只截取了啟動的時候兩個方法一個初始化,一個是開始啟動,在初始化之前的 init 方法是初始化一些環境參數。這里就沒有詳細說明。我們先看

if (command.equals("startd")) {
                args[args.length - 1] = "start";
                //先初始化一個必要的組件 如server connetor service
                //這里的daemon 是就是 Bootstrap自己
                daemon.load(args);
                //開始加載Engine 下面相關東西,像Host Conetxt weapp
                daemon.start();
            }

我們再往下看Bootstrap 的 load函數

private void load(String[] arguments)
        throws Exception {

        // Call the load() method
      
        String methodName = "load";
        Object param[];
        Class<?> paramTypes[];
        if (arguments==null || arguments.length==0) {
           paramTypes = null;
            param = null;
        } else {
            paramTypes = new Class[1];
            paramTypes[0] = arguments.getClass();
            param = new Object[1];
            param[0] = arguments;
        }
      // 反射獲取catalina 的對象的 load
        Method method =
            catalinaDaemon.getClass().getMethod(methodName, paramTypes);
        if (log.isDebugEnabled())
            log.debug("Calling startup class " + method);
        //利用反射去調用 catalina的load方法
        method.invoke(catalinaDaemon, param);
 }

然后下一步我們去CatalinaDaemon驗證下是不是有一個無參的load方法。
果然有一個load方法(方法代碼太多只截取重要的部分)

public void load() {
//創建一個xml解析器
  Digester digester = createStartDigester();
        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        try {
            //讀取conf/server.xml文件
            file = configFile();
            inputStream = new FileInputStream(file);
            inputSource = new InputSource(file.toURI().toURL().toString());
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.debug(sm.getString("catalina.configFail", file), e);
            }
        }
····· //這里的一些代碼是保證讀到server.xml文件的內容
 try {
            inputSource.setByteStream(inputStream);
            digester.push(this);
            //解析 配置文件流,對配置文件里面的配置進行初始化
            digester.parse(inputSource);
        } catch (SAXParseException spe) {
            log.warn("Catalina.start using " + getConfigFile() + ": " +
                    spe.getMessage());
            return;
        } catch (Exception e) {
            log.warn("Catalina.start using " + getConfigFile() + ": " , e);
            return;
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                // Ignore
            }
        }
       ·············· 下面就是開始 Server的初始化了
  // Start the new server 
        try {
            getServer().init();
        } catch (LifecycleException e) {
            if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                throw new java.lang.Error(e);
            } else {
                log.error("Catalina.start", e);
            }

        }

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

我們跟到里面getServer().init();看看做了什么,進去之后發現到了"Lifecycle" 類的 init()方法 這是一個接口,這個接口有三個實現類
除了 LifecycleBase里面是有現實,其他兩個類都沒有實現,那就不用肯定是調用LifecycleBase的init方法了。

public final synchronized void init() throws LifecycleException {
        //默認的實現
        if (!state.equals(LifecycleState.NEW)) {
            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
        }
        setStateInternal(LifecycleState.INITIALIZING, null, false);

        try {
            //爭對所有的子類初始化,這個地方調用的是自己的抽象方法。
            initInternal();
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(
                    sm.getString("lifecycleBase.initFail",toString()), t);
        }

        setStateInternal(LifecycleState.INITIALIZED, null, false);
    }

點開 LifecycleBase類的 initInternal()方法,發現也是有很多實現,其實到這里我們應該去看哪個實現類了,肯定是跟Server有關的。于是乎我們就找到了
StandardServer 這個類的initInternal() 方法

  protected void initInternal() throws LifecycleException {
    //調用父類的 方法
     super.initInternal();

    // 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
  //結合之前的tomcat 體系結構圖中來看  globalNamingResources資源文件組件進行初始化
      globalNamingResources.init();
   ··············· 此處代碼省略
    //這個地方就驗證了官網說的 Server下面有多個Service 這的調
         //service 里面的init方法
        // Initialize our defined Services 初始化多個services
        for (int i = 0; i < services.length; i++) {
              services[i].init();
        }
      }
這里Service 調用的init 方法是 Lifecycle的,
之前我們說Lifecycle init      是LifecycleBase實現了
我們看下LifecycleBase 的init 方法
public final synchronized void init() throws LifecycleException {
    //默認的實現
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }
    setStateInternal(LifecycleState.INITIALIZING, null, false);

    try {
        //初始化  Service 組件
        initInternal();
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(
                sm.getString("lifecycleBase.initFail",toString()), t);
    }
  setStateInternal(LifecycleState.INITIALIZED, null, false);
}

我們發現這個initInternal() 方法又是 LifecycleBase的,所以我們在找對應的實現類的時候,就可以參照之前方法,找跟service 相關的,如果只有一個StandardService類

protected void initInternal() throws LifecycleException{
······此處代碼省略
//這里是線程池的初始化,這個可以對照上面的圖來看
 for (Executor executor : findExecutors()) {
            if (executor instanceof JmxEnabled) {
                ((JmxEnabled) executor).setDomain(getDomain());
            }
            //線程池 初始化
            executor.init();
        }
 // 初始化 mapper listener
        mapperListener.init();

        // Initialize our defined Connectors
       //可以看到這里的connector 是多個的,用加鎖來保證線程安全
        synchronized (connectorsLock) {
            for (Connector connector : connectors) {
                try {
                    //connector就開始初始化了
                    connector.init();
                } catch (Exception e) {
                    String message = sm.getString(
                            "standardService.connector.initFailed", connector);
                    log.error(message, e);

                    if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                        throw new LifecycleException(message);
                }
            }
        }
}

這里調用每個 Connector的init方法,再一步步跟進去,發現還是之前的套路,
在Connector 的init 方法里面又會去調用 LifecycleBase 的 initInternal() 方法,這次我們還是去找Connector里面的實現
還是貼代碼

protected void initInternal() throws LifecycleException {
            //其實到這里必要的組件在這個方法里面已經初始化完了
        //bootstarp 方法 load 方法已經完成了
      //這里還是老樣子調用父類LifecycleMBeanBase 去注冊bean
        super.initInternal();

        // Initialize adapter 這里就是初始化處理器
        adapter = new CoyoteAdapter(this);
          //綁定處理器 然后就是到下面 init 方法再去綁定
        protocolHandler.setAdapter(adapter);
        ·········此處代碼省略
        try {
            //決定是用bio 還是nio  nio2 apr 這些組件去處理請求
          //我們看看這個里面做了些什么 
            protocolHandler.init();
        } catch (Exception e) {
            throw new LifecycleException
                (sm.getString
                 ("coyoteConnector.protocolHandlerInitializationFailed"), e);
        }
    }

protocolHandler 這個類是一個接口,有兩個實現類,我們肯定是找
AbstractProtocol這個實現類 的init 方法,在這個方法里面有一段代碼也就是最后

try {
            //在這里去調用,這里就是具體的綁定用 Apr 還是JIo 還是NIo
            endpoint.init();
        } catch (Exception ex) {
            getLog().error(sm.getString("abstractProtocolHandler.initError",
                    getName()), ex);
            throw ex;
        }

那么到這里初始化的工作就全部做完了,看到這里肯定暈了。


image.png

Connector 、StandardService、StandardServer 他們都是Lifecycle的子類,其實到最后才發現整個 初始化過程都是圍繞著 Lifecycle init() 和 LifecycleBase initInternal() 方法來實現的。
其實這個初始化的過程,就是用 責任鏈的調用模式來實現的, 一個鏈接著一個鏈來的任務來,最終完成所有的初始化。


華麗的分割線

初始化完成了,那我們再去看看啟動,回到 Bootstrap 的main 方法

不說廢話直接上圖

if (command.equals("startd")) {
                args[args.length - 1] = "start";
                //先初始化一個必要的組件 如server connetor service
                //這里的daemon 是就是 Bootstrap自己
                daemon.load(args);
                //開始加載Engine 下面相關東西,像Host Conetxt weapp
                daemon.start();
            }
public void start()
        throws Exception {
        //還是跟之前是一樣的方法 反射調用catalina里面的start 方法
        if( catalinaDaemon==null ) init();

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

    }

我們再看 catalina 里面的start 方法

 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();
        //前面都是做一些判空的操作,下面server 開始啟動
        // Start the new server
        try {
            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;
        }
    }

我們按照之前的思維來分析 這個start 方法肯定又是Lifecycle 接口的一個方法,我們再去找相關的實現類 LifecycleBase,在代碼中又調用了自己的startInternal方法,所以我們還是找相關的實現類StandardServer

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++) {
                // 跟之前的方式一樣 ,開始每一個service啟動
                services[i].start();
            }
        }
    }

我們跟到StandardService 的 startInternal方法

protected void startInternal() throws LifecycleException {

        if(log.isInfoEnabled())
            log.info(sm.getString("standardService.start.name", this.name));
        setState(LifecycleState.STARTING);

        // Start our defined Container first
        if (container != null) {
            synchronized (container) {
              //這個地方就是 啟動Engine、Host、context 這些組件
                container.start();
            }
        }

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

        //以及一些監聽器
        mapperListener.start();

        // Start our defined Connectors second
        synchronized (connectorsLock) {
            for (Connector connector: connectors) {
                try {
                    // If it has already failed, don't try and start it
                    if (connector.getState() != LifecycleState.FAILED) {
                        //啟動container 連接器管理
                        connector.start();
                    }
                } catch (Exception e) {
                    log.error(sm.getString(
                            "standardService.connector.startFailed",
                            connector), e);
                }
            }
        }
    }

我們可以看到 connector 和 executor 的組件啟動。
這里我們繼續 Connector 的 startInternal() 方法

protected void startInternal() throws LifecycleException {

        // Validate settings before starting
        if (getPort() < 0) {
            throw new LifecycleException(sm.getString(
                    "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
        }

        setState(LifecycleState.STARTING);

        try {
            //這里就跟處理請求的,模式 是Bio NIO ···等方式, 這個在之前初始化的時候就已經選擇好了
            protocolHandler.start();
        } catch (Exception e) {
            String errPrefix = "";
            if(this.service != null) {
                errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
            }

            throw new LifecycleException
                (errPrefix + " " + sm.getString
                 ("coyoteConnector.protocolHandlerStartFailed"), e);
        }
    }

上面代碼中 我們先看下 container.start() 這里,點進去發現這里是調用的ContainerBase 這個類startInternal()方法

protected synchronized void startInternal() throws LifecycleException {

        // Start our subordinate components, if any
        logger = null;
        getLogger();
        Cluster cluster = getClusterInternal();
        if ((cluster != null) && (cluster instanceof Lifecycle))
            ((Lifecycle) cluster).start();
        Realm realm = getRealmInternal();
        if ((realm != null) && (realm instanceof Lifecycle))
            ((Lifecycle) realm).start();

        // Start our child containers, if any
        Container children[] = findChildren();
        List<Future<Void>> results = new ArrayList<>();
        //通過責任鏈的 調用  不同的調用子類的start
        //這里的子類 有 Engine、Host、context 這些
        for (int i = 0; i < children.length; i++) {
            results.add(startStopExecutor.submit(new StartChild(children[i])));
        }

        boolean fail = false;
        for (Future<Void> result : results) {
            try {
                result.get();
            } catch (Exception e) {
                log.error(sm.getString("containerBase.threadedStartFailed"), e);
                fail = true;
            }

        }
        if (fail) {
            throw new LifecycleException(
                    sm.getString("containerBase.threadedStartFailed"));
        }

        // Start the Valves in our pipeline (including the basic), if any
        if (pipeline instanceof Lifecycle)
            ((Lifecycle) pipeline).start();


        setState(LifecycleState.STARTING);

        // Start our thread
        threadStart();

    }

關注 new StartChild(children[i] )這段代碼
這里會調用 engine 子容器的 start 方法。也就是Host,Host 會將一個一個的項目編譯后的class 文件加進來

 protected synchronized void startInternal() throws LifecycleException {

        // Set error report valve
        //
        String errorValve = getErrorReportValveClass();
        if ((errorValve != null) && (!errorValve.equals(""))) {
            try {
                boolean found = false;
                Valve[] valves = getPipeline().getValves();
                for (Valve valve : valves) {
                    if (errorValve.equals(valve.getClass().getName())) {
                        found = true;
                        break;
                    }
                }
                if(!found) {
                    Valve valve =
                        (Valve) Class.forName(errorValve).newInstance();
                    getPipeline().addValve(valve);
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error(sm.getString(
                        "standardHost.invalidErrorReportValveClass",
                        errorValve), t);
            }
        }
        super.startInternal();
    }

最后又去調用父類的startInternal(),然后 調用 父類里面的threadStart() 方法

protected void threadStart() {

        if (thread != null)
            return;
        if (backgroundProcessorDelay <= 0)
            return;

        threadDone = false;
        String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
        //每個子類的run方法  任務
        thread = new Thread(new ContainerBackgroundProcessor(), threadName);
        thread.setDaemon(true);
        thread.start();

    }

ContainerBackgroundProcessor 是實現了 Runnable d 線程類
processChildren 方法 里面去處理 各個子類的線程
container.backgroundProcess(); 這個是各個子類的事件的觸發器
這個方法最后 fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null)
這一段是調用LifecycleSupport 類

public void fireLifecycleEvent(String type, Object data) {

        LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
        LifecycleListener interested[] = listeners;
        for (int i = 0; i < interested.length; i++)
            interested[i].lifecycleEvent(event);

    }

然后又會用調用 LifecycleListener lifecycleEvent這個接口的方法
這個接口里面我們看到有很多實現


image.png

其實可以看出很多子類的具體任務都是在這里處理的。
我們找一下實現 HostConfig 里面的實現 lifecycleEvent方法
的check()方法

protected void check() {

        if (host.getAutoDeploy()) {
            // Check for resources modification to trigger redeployment
            DeployedApplication[] apps =
                deployed.values().toArray(new DeployedApplication[0]);
            for (int i = 0; i < apps.length; i++) {
                if (!isServiced(apps[i].name))
                    checkResources(apps[i]);
            }

            // Check for old versions of applications that can now be undeployed
            if (host.getUndeployOldVersions()) {
                checkUndeploy();
            }

            // Hotdeploy applications
            //開始部署應用
            deployApps();
        }
    }

這個地方就是開始部署應用了。

protected void deployApps() {
        //部署web 項目
        File appBase = host.getAppBaseFile();
        //找到config的配置文件路徑
        File configBase = host.getConfigBaseFile();
        String[] filteredAppPaths = filterAppPaths(appBase.list());
        // Deploy XML descriptors from configBase
        deployDescriptors(configBase, configBase.list());
        // Deploy WARs
        //這個filteredAppPaths 是返回這個路徑下面 所有的文件夾及文件名
        deployWARs(appBase, filteredAppPaths);
        // Deploy expanded folders
        deployDirectories(appBase, filteredAppPaths);

    }

到這個地方 整個tomcat 的啟動流程就完了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內容