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的啓動的概要流程圖可以表示如下: