tomcat容器啓動 原

tomcat文件結構

其中conf中有一些比較重要的文件,如下:

catalina.properties Tomcat環境變量配置
catalina.policy 安全模式下運行時的默認安全策略
context.xml 所有web應用需要加載的Context配置,如果用戶指定了自己的Context,這個文件將會被覆蓋
logging.properties Tomcat日誌文件,可以修改tomcat的日誌級別
Server.xml Tomcat的很很重要的配置文件,配置連接器,監聽端口,服務器實例等,tomcat優化相關也均會配置在此文件中
tomcat-users.xml Tomcat manager認證
web.xml Tomcat默認的web.xml文件,定義了基礎的Servlet和MIME映射。如果應用中沒有web.xml,則tomcat會使用此文件作爲默認文件

啓動腳本

當我們下載下來tomcat以後,在bin文件夾下,tomcat分別提供了linux和windows的啓動腳本文件,startup.bat和startup.sh,這個文件沒有實質性的用處,主要是爲了引導執行catalina.sh(或者catalina.bat)文件,所以我們在啓動tomcat時,也可以直接啓動catalina.sh。這個文件內容特別多,有500多行,是tomcat啓動的核心文件。主要分爲以下幾方面內容

1.   設置CATALINA_HOME和CATALINA_BASE

2.   尋找是否有setevn.sh和setclasspath.sh文件,並加載,我們自定義的tomcat參數,例如設置jvm參數的大小,就可以在新建一個setevn.sh文件,然後在這個文件中配置,tomcat會自動加載這個文件。setclasspath.sh是一些必要的批處理腳本,它只負責查找和檢查JAVA_HOME和JRE_HOME兩個環境變量。

3.   設置日誌組件。

4.   根據不同的配置組裝最後要啓動的命令參數。

5.   執行啓動命令

 啓動類

 tomcat的啓動類是Bootstrap.java,類首先通過靜態方法加載catalina.base和catalina.home的位置,然後實例化Bootstarp類,通過調用init方法進行初始化。初始化時,加載

${catalina.base}/lib,${catalina.base}/lib/*.jar,"${catalina.home}/lib,${catalina.home}/lib/*.jar 這些文件夾以及jar文件。並創建tomcat原始的ClassLoader。然後通過classLoader實例化org.apache.catalina.startup.Catalina類並進行初始化其parentClassLoader,然後此函數線程作爲守護線程。

默認的啓動參數是start,當參數是start時代表是啓動tomcat,實際上是調用Catalina.java的load()和start()函數進行啓動。接下來分別看一下這兩個函數

load()函數

load函數會實例化出來一個Server實例。每個tomcat只能有一個server實例。server節點是server.xml的根節點,tomcat所有的配置也都是在這這個節點之下,下屬節點主要分爲連接器,監聽器,容器這幾類。

public void load() {

        long t1 = System.nanoTime();
		//設置臨時文件目錄
        initDirs();
		
        // 初始化digester框架可能用的一些naming信息
        initNaming(); 

        // 創建Digester對象,並且做節點和相關實體類的映射對照關係
        Digester digester = createStartDigester();

        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        try {
			
            	//巴拉巴拉一大堆,就是爲了加載server.xml文件到digester中
            }

            try {
                inputSource.setByteStream(inputStream);
                digester.push(this);
                digester.parse(inputSource);
            } catch (SAXParseException spe) {
               
            }
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }
		
	 
	/**
初始化Server的一些最基本的信息
 server指的是org.apache.catalina.Server,這個也就是tomcat的頂級容器
	**/
        getServer().setCatalina(this);
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

        // Stream redirection
        initStreams();
        try {
        	/**
        	 * 初始化Server信息,Server是一個接口,默認實現是StandardServer
        	 * StandardServer繼承自抽象類LifecycleBase,init方法由其實現,tomcat的容器的默認實現均繼承自此抽象類,並且實現lifecycle接口,LifecycleBase中有抽象方法startInternal()由其子類進行實現
        	 * 具體的針對tomcat容器的啓動方式稍後再做分析
        	 */
            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);
            }
        }

start函數

public void start() {
    	
    	//重複的加載server,以保證server不是空的,重複加載以後如果server依然爲空,則拋出異常,啓動失敗
        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 {
        	//啓動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
        if (useShutdownHook) {
            if (shutdownHook == null) {
                shutdownHook = new CatalinaShutdownHook();
            }
            //增加關閉鉤子,tomcat在關閉(不管是正常還是異常)時執行後續的收尾工作
            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);
            }
        }
        if (await) {
        	//開啓ServerSocket,等待請求,並接收請求
            await();
            //去除關閉鉤子,逐級關閉容器
            stop();
        }
    }

tomcat的容器以及啓動

tomcat有四種容器,這個通過server.xml可以看出來

容器均實現Lifecycle接口,它的啓動,是在通過調用StandardServer load方法時進行初始化,通過調用start方法進行啓動。在Lifecycle的開頭註釋部分,非常明確的畫出了整個容器的聲明週期過程。

首先容器需要先調用init()方法進行初始化,初始化完成之後調用start()方法啓動容器,當容器不再使用,調用stop()進行停止, 最後destory()方法銷燬容器

在啓動的過程中,由於涉及到很多的事件,對於各種事件的處理不可能硬編碼到代碼中,並且了爲了可擴展,lifecycle定義了很多的事件監聽點,用到自定義實現的觀察者設計模式。各個事件的監聽器可以註冊自己感興趣的事件,當這事件發生時會進行通知。

生命週期時期

調用事件

init

在init之前會調用前後會分別調用註冊了以下事件的方法

BEFORE_INIT_EVENT

AFTER_INIT_EVENT

start

在接收到start命令時,調用START_EVENT

Start之前調用BEFORE_START_EVENT,容器啓動之後調用註冊了AFTER_START_EVENT事件的方法。

stop

接收到stop命令,會調用STOP_EVENT的事件,

Stop之前調用BEFORE_STOP_EVENT,之後調用AFTER_STOP_EVENT

destroy

容器銷燬之前調用AFTER_DESTROY_EVENT,銷燬之後調用感興趣的註冊了BEFORE_DESTROY_EVENT事件的類的方法

除了以上還提供了PERIODIC_EVENT事件,這個註冊此時間的方法會被週期性的執行,配合CONFIGURE_START_EVENT和CONFIGURE_STOP_EVENT事件的使用,可以監控配置文件是否修改,然後進行熱部署的效果。

 

tomcat容器的啓動採用的是鏈式啓動結構,有父容器進行查找其下面的字容器進行啓動。例如我們啓動Engine,它會自動的找它下面的Host,啓動Engine的同時進行啓動host。

容器均實現Container接口, ContainerBase,以Standardxxx開頭的類是容器的默認實現,Container接口中有一個findChinldren()方法,用來查找這個容器下的子容器,默認由ContainerBase實現,container接口有一個默認的抽象實現類ContainerBase實現,而findChinldren()方法也默認由其實現。容器的默認實現Standardxxx 繼承自ContainerBase。

 

而整個tomcat的啓動的概要流程圖可以表示如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章