從server.xml文件解析出來的各個對象都是容器,比如:Server、Service、Connector等。這些容器都具有新建、初始化完成、啟動、停止、失敗、銷毀等狀態。tomcat的實現提供了對這些容器的生命周期管理,本文將通過對Tomcat7.0的源碼閱讀,深入剖析這一過程。
我們先閱讀圖1,從中了解Tomcat涉及生命周期管理的主要類。
圖1 Tomcat生命周期類接口設計
這里對圖1中涉及的主要類作個簡單介紹:
此外,ContainerBase、StandardServer、StandardService、WebappLoader、Connector、StandardContext、StandardEngine、StandardHost、StandardWrapper等容器都繼承了LifecycleMBeanBase,因此這些容器都具有了同樣的生命周期并可以通過JMX進行管理。
java管理程序擴展(java management extensions,簡稱JMX),是一個可以為Java應用程序或系統植入遠程管理功能的框架。為便于講解,我從網絡上找了一張JMX的架構,如圖2所示。
圖2 JMX架構
這里對圖2中三個分層進行介紹:
StandardServer、StandardService、Connector、StandardContext這些容器,彼此之間都有父子關系,每個容器都可能包含零個或者多個子容器,這些子容器可能存在不同類型或者相同類型的多個,如圖3所示。
圖3 Tomcat容器組成
目前,Tomcat的容器具有以下狀態:
這些狀態都定義在枚舉類LifecycleState中。
每個容器由于繼承自LifecycleBase,當容器狀態發生變化時,都會調用fireLifecycleEvent方法,生成LifecycleEvent,并且交由此容器的事件監聽器處理。LifecycleBase的fireLifecycleEvent方法的實現見代碼清單1。
代碼清單1
/** * 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方法的實現,見代碼清單2。
代碼清單2
/** * 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. gja * * @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); }
代碼清單2將事件通知給所有監聽當前容器的生命周期監聽器LifecycleListener,并調用LifecycleListener的lifecycleEvent方法。每個容器都維護這一個監聽器緩存,其實現如下:
/** * The set of registered LifecycleListeners for event notifications. */ private LifecycleListener listeners[] = new LifecycleListener[0];
那么listeners中的監聽器是何時添加進來的呢?每個容器在新建、初始化、啟動,銷毀,被添加到父容器的過程中都會調用父類LifecycleBase的addLifecycleListener方法,addLifecycleListener的實現見代碼清單3。
代碼清單3
@Override public void addLifecycleListener(LifecycleListener listener) { lifecycle.addLifecycleListener(listener); }
從代碼清單3看到,LifecycleBase的addLifecycleListener方法實際是對LifecycleSupport的addLifecycleListener方法的簡單代理,LifecycleSupport的addLifecycleListener方法的實現,見代碼清單4。
代碼清單4
/** * Add a lifecycle event listener to this component. * * @param listener The listener to add */ public void addLifecycleListener(LifecycleListener listener) { synchronized (listenersLock) { LifecycleListener results[] = new LifecycleListener[listeners.length + 1]; for (int i = 0; i < listeners.length; i++) results[i] = listeners[i]; results[listeners.length] = listener; listeners = results; } }
在代碼清單2中,我們講過容器會最終調用每個對此容器感興趣的LifecycleListener的lifecycleEvent方法,那么LifecycleListener的lifecycleEvent方法會做些什么呢?為了簡單起見,我們以監聽器AprLifecycleListener為例,AprLifecycleListener的lifecycleEvent方法的實現,見代碼清單5。
代碼清單5
/** * Primary entry point for startup and shutdown events. * * @param event The event that has occurred */ public void lifecycleEvent(LifecycleEvent event) { if (Lifecycle.INIT_EVENT.equals(event.getType())) { synchronized (lock) { init(); if (aprAvailable) { try { initializeSSL(); } catch (Throwable t) { log.info(sm.getString("aprListener.sslInit")); } } } } else if (Lifecycle.AFTER_STOP_EVENT.equals(event.getType())) { synchronized (lock) { if (!aprAvailable) { return; } try { terminateAPR(); } catch (Throwable t) { log.info(sm.getString("aprListener.aprDestroy")); } } } }
每個容器都會有自身的生命周期,其中也涉及狀態的遷移,以及伴隨的事件生成,本節詳細介紹Tomcat中的容器生命周期實現。所有容器的轉態轉換(如新疆、初始化、啟動、停止等)都是由外到內,由上到下進行,即先執行父容器的狀態轉換及相關操作,然后再執行子容器的轉態轉換,這個過程是層層迭代執行的。
所有容器在構造的過程中,都會首先對父類LifecycleBase進行構造。LifecycleBase中定義了所有容器的起始狀態為LifecycleState.NEW,代碼如下:
/** * The current state of the source component. */ private volatile LifecycleState state = LifecycleState.NEW;
每個容器的init方法是自身初始化的入口,其初始化過程如圖4所示。
圖4 容器初始化時序圖
圖4中所說的具體容器,實際就是LifecycleBase的具體實現類,目前LifecycleBase的類繼承體系如圖5所示。
圖5 LifecycleBase的類繼承體系
根據圖4所示的初始化過程,我們對Tomcat的源碼進行分析,其處理步驟如下:
現在對容器初始化的源碼進行分析,init方法的實現見代碼清單6。
代碼清單6
public synchronized final void init() throws LifecycleException { if (!state.equals(LifecycleState.NEW)) { invalidTransition(Lifecycle.INIT_EVENT); } initInternal(); setState(LifecycleState.INITIALIZED); }
代碼清單6說明,只有當前容器的狀態處于LifecycleState.NEW的才可以被初始化,真正執行初始化的方法是initInternal,當初始化完畢,當前容器的狀態會被更改為LifecycleState.INITIALIZED。為了簡便起見,我們還是以StandardServer這個容器為例,StandardServer的initInternal方法的實現見代碼清單7。
代碼清單7
@Override protected void initInternal() throws LifecycleException { super.initInternal(); // Register global String cache geng // 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 onameMBeanFactory = register(new MBeanFactory(), "type=MBeanFactory"); // Register the naming resources onameNamingResoucres = register(globalNamingResources, "type=NamingResources"); // Initialize our defined Services for (int i = 0; i < services.length; i++) { services[i].init(); } }
通過分析StandardServer的initInternal方法,其處理過程如下:
步驟一 將當前容器注冊到JMX
調用父類LifecycleBase的initInternal方法(見代碼清單8),為當前容器創建DynamicMBean,并注冊到JMX中。
代碼清單8
@Override protected void initInternal() throws LifecycleException { // If oname is not null then registration has already happened via jiaan // preRegister(). if (oname == null) { mserver = Registry.getRegistry(null, null).getMBeanServer(); oname = register(this, getObjectNameKeyProperties()); } }
StandardServer實現的getObjectNameKeyProperties方法如下:
@Override protected final String getObjectNameKeyProperties() { return "type=Server"; }
LifecycleBase的register方法(見代碼清單9)會為當前容器創建對應的注冊名稱,以StandardServer為例,getDomain默認返回Catalina,因此StandardServer的JMX注冊名稱默認為Catalina:type=Server,真正的注冊在registerComponent方法中實現。
代碼清單9
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)創建DynamicMBean,并且注冊到MBeanServer,見代碼清單10。
代碼清單10
/** Register a component * XXX make it private * * @param bean * @param oname * @param type * @throws Exception */ 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
從代碼清單7中已經列出。其中StringCache的注冊名為Catalina:type=StringCache,MBeanFactory的注冊名為Catalina:type=MBeanFactory,globalNamingResources的注冊名為Catalina:type=NamingResources。
步驟三 初始化子容器
從代碼清單7中看到StandardServer主要對Service子容器進行初始化,默認是StandardService。
注意:個別容器并不完全遵循以上的初始化過程,比如ProtocolHandler作為Connector的子容器,其初始化過程并不是由Connector的initInternal方法調用的,而是與啟動過程一道被Connector的startInternal方法所調用。
每個容器的start方法是自身啟動的入口,其啟動過程如圖6所示。
圖6 容器啟動時序圖
根據圖6所示的啟動過程,我們對Tomcat的源碼進行分析,其處理步驟如下:
現在對容器啟動的源碼進行分析,start方法的實現見代碼清單11。
代碼清單11
@Override public synchronized final 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.INITIALIZED) && !state.equals(LifecycleState.STOPPED)) { invalidTransition(Lifecycle.BEFORE_START_EVENT); } setState(LifecycleState.STARTING_PREP); try { startInternal(); } catch (LifecycleException e) { setState(LifecycleState.FAILED); throw e; } if (state.equals(LifecycleState.FAILED) || state.equals(LifecycleState.MUST_STOP)) { stop(); } else { // Shouldn't be necessary but acts as a check that sub-classes are // doing what they are supposed to. if (!state.equals(LifecycleState.STARTING)) { invalidTransition(Lifecycle.AFTER_START_EVENT); } setState(LifecycleState.STARTED); } }
代碼清單11說明在真正啟動容器之前需要做2種檢查:
代碼清單11還說明啟動容器完畢后,需要做1種檢查,即如果容器啟動異常導致容器進入LifecycleState.FAILED或者LifecycleState.MUST_STOP狀態,則需要調用stop方法停止容器。
現在我們重點分析startInternal方法,還是以StandardServer為例,其startInternal的實現見代碼清單12所示。
代碼清單12
@Override protected void startInternal() throws LifecycleException { fireLifecycleEvent(CONFIGURE_START_EVENT, null); setState(LifecycleState.STARTING); // Start our defined Services synchronized (services) { for (int i = 0; i < services.length; i++) { services[i].start(); } } }
從代碼清單12看到StandardServer的啟動由以下步驟組成:
除了初始化、啟動外,各個容器還有停止和銷毀的生命周期,其原理與初始化、啟動類似,本文不再贅述,有興趣的讀者可以自行研究。
Tomcat啟動完畢后,打開Java visualVM,打開Tomcat進程監控,給visualVM安裝MBeans插件后,選擇MBeans標簽頁可以對Tomcat所有注冊到JMX中的對象進行管理,比如StandardService就向JMX暴露了start和stop等方法,這樣管理員就可以動態管理Tomcat,如圖7所示。
圖7 使用JMX動態管理Tomcat
Tomcat通過將內部所有組件都抽象為容器,為容器提供統一的生命周期管理,各個子容器只需要關心各自的具體實現,這便于Tomcat以后擴展更多的容器,對于研究或者學習Tomcat的人來說,其設計清晰易懂。
如需轉載,請標明本文作者及出處——作者:jiaan.gja,本文原創首發:博客園,原文鏈接:http://www.49028c.com/jiaan-geng/p/4864501.html
新聞熱點
疑難解答