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 的啟動流程就完了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容