前言
從server.xml文件解析出來的各個對象都是容器,比如:Server氧秘、Service年鸳、Connector等。這些容器都具有新建丸相、初始化完成搔确、啟動、停止灭忠、失敗膳算、銷毀等狀態(tài)。Tomcat的實現(xiàn)提供了對這些容器的生命周期管理更舞,本文將通過對Tomcat7.0的源碼閱讀畦幢,深入剖析這一過程。
Tomcat生命周期類接口設(shè)計
我們先閱讀圖1缆蝉,從中了解Tomcat涉及生命周期管理的主要類宇葱。
這里對圖1中涉及的主要類作個簡單介紹:
- Lifecycle:定義了容器生命周期、容器狀態(tài)轉(zhuǎn)換及容器狀態(tài)遷移事件的監(jiān)聽器注冊和移除等主要接口刊头;
- LifecycleBase:作為Lifecycle接口的抽象實現(xiàn)類黍瞧,運用抽象模板模式將所有容器的生命周期及狀態(tài)轉(zhuǎn)換銜接起來,此外還提供了生成LifecycleEvent事件的接口原杂;
- LifecycleSupport:提供有關(guān)LifecycleEvent事件的監(jiān)聽器注冊印颤、移除,并且使用經(jīng)典的監(jiān)聽器模式穿肄,實現(xiàn)事件生成后觸達監(jiān)聽器的實現(xiàn)年局;
- MBeanRegistration:Java JMX框架提供的注冊MBean的接口咸产,引入此接口是為了便于使用JMX提供的管理功能矢否;
- LifecycleMBeanBase:Tomcat提供的對MBeanRegistration的抽象實現(xiàn)類,運用抽象模板模式將所有容器統(tǒng)一注冊到JMX脑溢;
此外僵朗,ContainerBase、StandardServer屑彻、StandardService验庙、WebappLoader、Connector社牲、StandardContext粪薛、StandardEngine、StandardHost搏恤、StandardWrapper等容器都繼承了LifecycleMBeanBase违寿,因此這些容器都具有了同樣的生命周期并可以通過JMX進行管理让禀。
什么是JMX?
java管理程序擴展(java management extensions陨界,簡稱JMX),是一個可以為Java應(yīng)用程序或系統(tǒng)植入遠程管理功能的框架痛阻。為便于講解菌瘪,我從網(wǎng)絡(luò)上找了一張JMX的架構(gòu),如圖2所示阱当。
這里對圖2中三個分層進行介紹:
- Probe Level:負責資源的檢測(獲取信息)俏扩,包含MBeans,通常也叫做Instrumentation Level弊添。MX管理構(gòu)件(MBean)分為四種形式录淡,分別是標準管理構(gòu)件(Standard MBean)、動態(tài)管理構(gòu)件(Dynamic MBean)油坝、開放管理構(gòu)件(Open Mbean)和模型管理構(gòu)件(Model MBean)嫉戚。
- Agent Level:即MBeanServer,是JMX的核心澈圈,負責連接Mbeans和應(yīng)用程序彬檀。
- Remote Management Level:通過connectors和adaptors來遠程操作MBeanServer,常用的控制臺瞬女,例如JConsole窍帝、VisualVM等。
容器
Tomcat容器組成
StandardServer诽偷、StandardService坤学、Connector、StandardContext這些容器报慕,彼此之間都有父子關(guān)系深浮,每個容器都可能包含零個或者多個子容器,這些子容器可能存在不同類型或者相同類型的多個卖子,如圖3所示略号。
Tomcat容器狀態(tài)
目前,Tomcat的容器具有以下狀態(tài):
- NEW:容器剛剛創(chuàng)建時洋闽,即在LifecycleBase實例構(gòu)造完成時的狀態(tài)玄柠。
- INITIALIZED:容器初始化完成時的狀態(tài)。
- STARTING_PREP:容器啟動前的狀態(tài)诫舅。
- STARTING:容器啟動過程中的狀態(tài)羽利。
- STARTED:容器啟動完成的狀態(tài)。
- STOPPING_PREP:容器停止前的狀態(tài)刊懈。
- STOPPING:容器停止過程中的狀態(tài)这弧。
- STOPPED:容器停止完成的狀態(tài)娃闲。
- DESTROYED:容器銷毀后的狀態(tài)。
- FAILED:容器啟動匾浪、停止過程中出現(xiàn)異常的狀態(tài)皇帮。
- MUST_STOP:此狀態(tài)未使用。
- MUST_DESTROY:此狀態(tài)未使用蛋辈。
這些狀態(tài)都定義在枚舉類LifecycleState中属拾。
事件與監(jiān)聽
每個容器由于繼承自LifecycleBase,當容器狀態(tài)發(fā)生變化時冷溶,都會調(diào)用fireLifecycleEvent方法渐白,生成LifecycleEvent,并且交由此容器的事件監(jiān)聽器處理逞频。LifecycleBase的fireLifecycleEvent方法的實現(xiàn)見代碼:
/**
* Allow sub classes to fire {@link Lifecycle} events.
*
* @param type Event type
* @param data Data associated with event.
*/
protected void fireLifecycleEvent(String type, Object data) {
lifecycle.fireLifecycleEvent(type, data);
}
lifecycle的定義如下:
/**
* Used to handle firing lifecycle events.
* TODO: Consider merging LifecycleSupport into this class.
*/
private LifecycleSupport lifecycle = new LifecycleSupport(this);
LifecycleSupport的fireLifecycleEvent方法的實現(xiàn)
/**
* Notify all lifecycle event listeners that a particular event has
* occurred for this Container. The default implementation performs
* this notification synchronously using the calling thread.
*
* @param type Event type
* @param data Event data
*/
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);
}
事件通知給所有監(jiān)聽當前容器的生命周期監(jiān)聽器LifecycleListener纯衍,并調(diào)用LifecycleListener的lifecycleEvent方法。
容器生命周期
每個容器都會有自身的生命周期苗胀,其中也涉及狀態(tài)的遷移襟诸,以及伴隨的事件生成,本節(jié)詳細介紹Tomcat中的容器生命周期實現(xiàn)基协。所有容器的轉(zhuǎn)態(tài)轉(zhuǎn)換(如新疆励堡、初始化、啟動堡掏、停止等)都是由外到內(nèi)应结,由上到下進行,即先執(zhí)行父容器的狀態(tài)轉(zhuǎn)換及相關(guān)操作泉唁,然后再執(zhí)行子容器的轉(zhuǎn)態(tài)轉(zhuǎn)換鹅龄,這個過程是層層迭代執(zhí)行的。
容器新建
所有容器在構(gòu)造的過程中亭畜,都會首先對父類LifecycleBase進行構(gòu)造扮休。LifecycleBase中定義了所有容器的起始狀態(tài)為LifecycleState.NEW,代碼如下:
/**
* The current state of the source component.
*/
private volatile LifecycleState state = LifecycleState.NEW;
容器初始化
每個容器的init方法是自身初始化的入口拴鸵,其初始化過程如圖4所示玷坠。
圖4中所說的具體容器,實際就是LifecycleBase的具體實現(xiàn)類劲藐,目前LifecycleBase的類繼承體系如圖5所示八堡。
根據(jù)圖4所示的初始化過程,我們對Tomcat的源碼進行分析聘芜,其處理步驟如下:
- 調(diào)用方調(diào)用容器父類LifecycleBase的init方法兄渺,LifecycleBase的init方法主要完成一些所有容器公共抽象出來的動作;
- LifecycleBase的init方法調(diào)用具體容器的initInternal方法實現(xiàn)汰现,此initInternal方法用于對容器本身真正的初始化挂谍;
- 具體容器的initInternal方法調(diào)用父類LifecycleMBeanBase的initInternal方法實現(xiàn)叔壤,此initInternal方法用于將容器托管到JMX,便于運維管理口叙;
- LifecycleMBeanBase的initInternal方法調(diào)用自身的register方法炼绘,將容器作為MBean注冊到MBeanServer;
- 容器如果有子容器妄田,會調(diào)用子容器的init方法饭望;
- 容器初始化完畢,LifecycleBase會將容器的狀態(tài)更改為初始化完畢形庭,即LifecycleState.INITIALIZED。
現(xiàn)在對容器初始化的源碼進行分析厌漂,init方法的實現(xiàn)
@Override
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
}
只有狀態(tài)為LifecycleState.NEW時才會初始化萨醒,真正執(zhí)行初始化的方法是initInternal,當初始化完畢苇倡,當前容器的狀態(tài)會被更改為LifecycleState.INITIALIZED富纸。為了簡便起見,我們還是以StandardServer這個容器為例旨椒,StandardServer的initInternal方法的實現(xiàn)
@Override
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
globalNamingResources.init();
// Populate the extension validator with JARs from common and shared
// class loaders
if (getCatalina() != null) {
ClassLoader cl = getCatalina().getParentClassLoader();
// Walk the class loader hierarchy. Stop at the system class loader.
// This will add the shared (if present) and common class loaders
while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
if (cl instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url : urls) {
if (url.getProtocol().equals("file")) {
try {
File f = new File (url.toURI());
if (f.isFile() &&
f.getName().endsWith(".jar")) {
ExtensionValidator.addSystemResource(f);
}
} catch (URISyntaxException e) {
// Ignore
} catch (IOException e) {
// Ignore
}
}
}
}
cl = cl.getParent();
}
}
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
通過分析StandardServer的initInternal方法晓褪,其處理過程如下:
步驟一 將當前容器注冊到JMX
調(diào)用父類LifecycleBase的initInternal方法(見代碼清單8),為當前容器創(chuàng)建DynamicMBean综慎,并注冊到JMX中涣仿。
@Override
protected void initInternal() throws LifecycleException {
// If oname is not null then registration has already happened via
// preRegister().
if (oname == null) {
mserver = Registry.getRegistry(null, null).getMBeanServer();
oname = register(this, getObjectNameKeyProperties());
}
}
StandardServer實現(xiàn)的getObjectNameKeyProperties方法如下:
@Override
protected final String getObjectNameKeyProperties() {
return "type=Server";
}
LifecycleBase的register方法會為當前容器創(chuàng)建對應(yīng)的注冊名稱,以StandardServer為例示惊,getDomain默認返回Catalina好港,因此StandardServer的JMX注冊名稱默認為Catalina:type=Server,真正的注冊在registerComponent方法中實現(xiàn)米罚。
protected final ObjectName register(Object obj,
String objectNameKeyProperties) {
// Construct an object name with the right domain
StringBuilder name = new StringBuilder(getDomain());
name.append(':');
name.append(objectNameKeyProperties);
ObjectName on = null;
try {
on = new ObjectName(name.toString());
Registry.getRegistry(null, null).registerComponent(obj, on, null);
} catch (MalformedObjectNameException e) {
log.warn(sm.getString("lifecycleMBeanBase.registerFail", obj, name),
e);
} catch (Exception e) {
log.warn(sm.getString("lifecycleMBeanBase.registerFail", obj, name),
e);
}
return on;
}
Registry的registerComponent方法會為當前容器(如StandardServer)創(chuàng)建DynamicMBean钧汹,并且注冊到MBeanServer
public void registerComponent(Object bean, ObjectName oname, String type)
throws Exception
{
if( log.isDebugEnabled() ) {
log.debug( "Managed= "+ oname);
}
if( bean ==null ) {
log.error("Null component " + oname );
return;
}
try {
if( type==null ) {
type=bean.getClass().getName();
}
ManagedBean managed = findManagedBean(bean.getClass(), type);
// The real mbean is created and registered
DynamicMBean mbean = managed.createMBean(bean);
if( getMBeanServer().isRegistered( oname )) {
if( log.isDebugEnabled()) {
log.debug("Unregistering existing component " + oname );
}
getMBeanServer().unregisterMBean( oname );
}
getMBeanServer().registerMBean( mbean, oname);
} catch( Exception ex) {
log.error("Error registering " + oname, ex );
throw ex;
}
}
步驟二 將StringCache、MBeanFactory录择、globalNamingResources注冊到JMX
其中StringCache的注冊名為Catalina:type=StringCache拔莱,MBeanFactory的注冊名為Catalina:type=MBeanFactory,globalNamingResources的注冊名為Catalina:type=NamingResources隘竭。
步驟三 初始化子容器
StandardServer主要對Service子容器進行初始化塘秦,默認是StandardService。
注意:個別容器并不完全遵循以上的初始化過程动看,比如ProtocolHandler作為Connector的子容器嗤形,其初始化過程并不是由Connector的initInternal方法調(diào)用的,而是與啟動過程一道被Connector的startInternal方法所調(diào)用弧圆。
容器啟動
每個容器的start方法是自身啟動的入口赋兵,其啟動過程如圖6所示笔咽。
根據(jù)圖6所示的啟動過程,我們對Tomcat的源碼進行分析霹期,其處理步驟如下:
- 調(diào)用方調(diào)用容器父類LifecycleBase的start方法叶组,LifecycleBase的start方法主要完成一些所有容器公共抽象出來的動作;
- LifecycleBase的start方法先將容器狀態(tài)改為LifecycleState.STARTING_PREP历造,然后調(diào)用具體容器的startInternal方法實現(xiàn)甩十,此startInternal方法用于對容器本身真正的初始化;
- 具體容器的startInternal方法會將容器狀態(tài)改為LifecycleState.STARTING吭产,容器如果有子容器侣监,會調(diào)用子容器的start方法啟動子容器;
- 容器啟動完畢臣淤,LifecycleBase會將容器的狀態(tài)更改為啟動完畢橄霉,即LifecycleState.STARTED。
現(xiàn)在對容器啟動的源碼進行分析邑蒋,start方法的實現(xiàn)
@Override
public final synchronized void start() throws LifecycleException {
if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
}
return;
}
if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}
try {
setStateInternal(LifecycleState.STARTING_PREP, null, false);
startInternal();
if (state.equals(LifecycleState.FAILED)) {
// This is a 'controlled' failure. The component put itself into the
// FAILED state so call stop() to complete the clean-up.
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
// This is an 'uncontrolled' failure so put the component into the
// FAILED state and throw an exception.
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
}
}
在真正啟動容器之前需要做2種檢查:
- 如果當前容器已經(jīng)處于啟動過程(即容器狀態(tài)為LifecycleState.STARTING_PREP姓蜂、LifecycleState.STARTING、LifecycleState.STARTED)中医吊,則會產(chǎn)生并且用日志記錄
- LifecycleException異常并退出钱慢。
如果容器依然處于LifecycleState.NEW狀態(tài),則在啟動之前卿堂,首先確保初始化完畢束莫。
啟動容器完畢后,需要做1種檢查草描,即如果容器啟動異常導(dǎo)致容器進入LifecycleState.FAILED或者LifecycleState.MUST_STOP狀態(tài)麦箍,則需要調(diào)用stop方法停止容器。
現(xiàn)在我們重點分析startInternal方法陶珠,還是以StandardServer為例挟裂,其startInternal的實現(xiàn)
@Override
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++) {
services[i].start();
}
}
}
上面代碼發(fā)現(xiàn)看到StandardServer的啟動由以下步驟組成:
- 產(chǎn)生CONFIGURE_START_EVENT事件;
- 將自身狀態(tài)更改為LifecycleState.STARTING揍诽;
- 調(diào)用子容器Service(默認為StandardService)的start方法啟動子容器诀蓉。
除了初始化、啟動外暑脆,各個容器還有停止和銷毀的生命周期渠啤,其原理與初始化、啟動類似添吗,本文不再贅述沥曹,有興趣的讀者可以自行研究。
總結(jié)
Tomcat通過將內(nèi)部所有組件都抽象為容器,為容器提供統(tǒng)一的生命周期管理妓美,各個子容器只需要關(guān)心各自的具體實現(xiàn)僵腺,這便于Tomcat以后擴展更多的容器,對于研究或者學習Tomcat的人來說壶栋,其設(shè)計清晰易懂辰如。