Tomcat容器的Server模塊有管理容器的啟動和關閉、管理了容器內的服務組件Service、管理了全域JNDI資源的功能,對Tomcat容器的生命周期管理有重要意義,Tomcat的服務組件則是Tomcat的兩個核心組件連接器和servlet容器之間的橋梁,本文會對Tomcat容器的服務器組件Server和服務組件Service進行介紹,
服務器組件Server
我們知道Tomcat容器啟動之后就可以一直保持服務,即使請求出現例外也不會退出,只有在收到特定的容器關閉命令時才會退出,Tomcat容器是怎么實作容器的啟動?啟動之后是如何保證容器一直保持運行?在收到容器關閉命令的時候怎么優雅關閉的呢?這就是Tomcat容器中的Server的功能了,
每個Tomcat容器都會唯一包含一個Server組件,對應于Tomcat安裝檔案夾下面的server.xml,下面為Tomcat10安裝包中conf/server.xml的默認配置,分析xml可知,server節點有 port和shutdown屬性,包含Listener、GlobalNamintResources和Service三部分子節點,下文我們會分別對這些內容進行介紹,
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
Server的啟動/停止
通過勺ò干以知道,Server組件重要的就是控制Tomcat容器的啟動/停止,然而啟動停止并不是簡單的啟動JVM關閉JVM就可以了,Tomcat容器啟動/停止是還必須呼叫容器內所有組件的生命周期方法,啟動時需要所有的組件進行初始化,結束時需要所有的組件進行銷毀和資源釋放,
JVM的啟動/停止
JVM的啟動比較簡單,我們在運行tomcat啟動腳本的時候,會啟動tomcat的Jar檔案,從而啟動JVM,
關于JVM的退出則稍微復雜一些,JVM退出的方式分為以下三種型別:
- 正常關閉:當最后一個非守護執行緒結束或者呼叫了System.exit或者通過其他特定平臺的方法關閉(發送SIGINT,SIGTERM信號等)
- 例外關閉:運行中遇到RuntimeException例外等,
- 強制關閉:通過呼叫Runtime.halt方法或者是在作業系統中直接kill(發送SIGKILL信號)掉JVM行程

對于正常關閉和例外關閉,JVM都有機會執行關閉的Hook方法,對于強制關閉則不一定會執行關閉時的hook方法,所以我們在日常使用中應該盡量避免使用kill -9等方法退出JVM,
JVM注冊Shutdown Hook的方法如下所示:
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
//
}
});
Tomcat容器啟動的時候會通過Runtime.getRuntime().addShutdownHook(Runnable run)方法向JVM注冊關倍訓呼方法CatalinaShutdownHook,從而實作容器的優雅關閉,
Tomcat關閉介面
我們上面講了JVM退出的情況下Tomcat怎么實作優雅的關閉,Tomcat也可以主動關閉程式,我們在配置server.xml檔案的時候,會指定server的port和shutdown指令,在需要關閉Tomcat容器的時候,我們只需要向指定埠發送關閉指令,Tomcat就會主動退出服務,
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
</Server>
生命周期的控制
Tomcat中需要實作宣告周期管理的組件都會實作Lifecycle介面,通過上文我們知道Tomcat的啟動/停止是由Server控制的,那么Server是如何通知容器內的其它組件(如container、connector)啟動/停止相關事件的呢?我們先看看Tomcat的結構圖,我們可以看到Tomcat容器的組件之間是一層層包含關系,一個Server包含多個Service,一個Service包含多個Container等等,
Tomcat容器在關閉的時候會通知所有的子組件(service組件)容器關閉事件,service組件再通知它的所有子組件容器關閉事件,事件通過父子關系層層傳遞到各個組件,從而實作組件之間的生命周期管理,

事實上Tomcat容器的生命周期事件不僅僅包含啟動/關閉,而是更詳細的劃分了啟動關閉的各個階段,分為以下代碼示例中的各個事件,
public static final String BEFORE_INIT_EVENT = "before_init";
public static final String AFTER_INIT_EVENT = "after_init";
public static final String START_EVENT = "start";
public static final String BEFORE_START_EVENT = "before_start";
public static final String AFTER_START_EVENT = "after_start";
public static final String STOP_EVENT = "stop";
public static final String BEFORE_STOP_EVENT = "before_stop";
public static final String AFTER_STOP_EVENT = "after_stop";
public static final String AFTER_DESTROY_EVENT = "after_destroy";
public static final String BEFORE_DESTROY_EVENT = "before_destroy";
public static final String PERIODIC_EVENT = "periodic";
public static final String CONFIGURE_START_EVENT = "configure_start";
public static final String CONFIGURE_STOP_EVENT = "configure_stop";
Service服務組件
Server中Service的配置如下所示,Service組件包含兩種組件:連接器和Servlet容器,其中servlet容器只有一個,連接器可以由多個,多個連接器可以使Tomcat為多種不同的請求協議提供服務,比如一個處理HTTP請求,另外一個處理HTTPS請求,
連接器負責將Socket請求決議為Request和Response,而Servlet容器則負責根據業務邏輯處理請求中的Request和Response,Service服務組件則負責把二者關聯起來,我會在其它文章中詳細介紹Servlet容器和連接器Connector,
每個連接器組件Connector都可以指定一個Servlet容器處理其決議得到的Request和Response,所以Service的功能比較簡單,就是為Service中的每個組件設定Servlet容器,
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
Server的其它配置
全域資源GlobalNamingResources
提供了容器級別的JNDI資源配置,比如下面的默認配置,就提供了Tomcat用戶資料的JNDI,存盤在conf/tomcat-users.xml中,容器資源對容器的依賴性比較高,現在的使用場景比較少,
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
監聽器Listener
監聽器用來監聽容器的特定事件,如容器的啟動關閉事件等,如下所示,默認的server.xml中包含了5個監聽器,我們接下來會簡單介紹默認監聽器的功能,
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
</Server>
- VersionLoggerListener:在容器啟動前列印各種版本資訊,如JVM版本、作業系統版本、tomcat版本等資訊,
- AprLifecycleListener:APR的生命周期處理,APR(Apache portable Run-time libraries,Apache可移植運行庫)的目的如其名稱一樣,主要為上層的應用程式提供一個可以跨越多作業系統平臺使用的底層支持介面庫,
- JreMemoryLeakPreventionListener:用于處理背景關系類加載器可能出現的記憶體泄露問題,啟動Java記憶體自動回收任務,每小時觸發FullGC,
- GlobalResourcesLifecycleListener:Tomcat啟動時實體化JNDI資源的MBean,Tomcat停止時銷毀MBean.
- ThreadLocalLeakPreventionListener:在Context關閉的時候清空執行緒背景關系,防止ThreadLocal記憶體泄露,
我是御狐神,歡迎大家關注我的微信公眾號:wzm2zsd

本文最先發布至微信公眾號,著作權所有,禁止轉載!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/300594.html
標籤:Java
