【Web容器】Tomcat源码分析(3)-生命周期管理

来源:互联网 发布:java微信 api好友列表 编辑:程序博客网 时间:2024/05/22 03:14

前言

  从server.xml文件解析出来的各个对象都是容器,比如:Server、Service、Connector等。这些容器都具有新建、初始化完成、启动、停止、失败、销毁等状态。tomcat的实现提供了对这些容器的生命周期管理,本文将通过对Tomcat7.0的源码阅读,深入剖析这一过程。

Tomcat生命周期类接口设计

  我们先阅读图1,从中了解Tomcat涉及生命周期管理的主要类。


图1  Tomcat生命周期类接口设计

这里对图1中涉及的主要类作个简单介绍:

  • Lifecycle:定义了容器生命周期、容器状态转换及容器状态迁移事件的监听器注册和移除等主要接口;
  • LifecycleBase:作为Lifecycle接口的抽象实现类,运用抽象模板模式将所有容器的生命周期及状态转换衔接起来,此外还提供了生成LifecycleEvent事件的接口;
  • LifecycleSupport:提供有关LifecycleEvent事件的监听器注册、移除,并且使用经典的监听器模式,实现事件生成后触打监听器的实现;
  • MBeanRegistration:Java jmx框架提供的注册MBean的接口,引入此接口是为了便于使用JMX提供的管理功能;
  • LifecycleMBeanBase:Tomcat提供的对MBeanRegistration的抽象实现类,运用抽象模板模式将所有容器统一注册到JMX;
此外,ContainerBase、StandardServer、StandardService、WebappLoader、Connector、StandardContext、StandardEngine、StandardHost、StandardWrapper等容器都继承了LifecycleMBeanBase,因此这些容器都具有了同样的生命周期并可以通过JMX进行管理。

什么是JMX?

  java管理程序扩展(java management extensions,简称JMX),是一个可以为Java应用程序或系统植入远程管理功能的框架。为便于讲解,我从网络上找了一张JMX的架构,如图2所示。


图2  JMX架构

这里对图2中三个分层进行介绍:
  • Probe Level:负责资源的检测(获取信息),包含MBeans,通常也叫做Instrumentation Level。MX管理构件(MBean)分为四种形式,分别是标准管理构件(Standard MBean)、动态管理构件(Dynamic MBean)、开放管理构件(Open Mbean)和模型管理构件(Model MBean)。
  • Agent Level:即MBeanServer,是JMX的核心,负责连接Mbeans和应用程序。 
  • Remote Management Level:通过connectors和adaptors来远程操作MBeanServer,常用的控制台,例如JConsole、VisualVM等。

容器

Tomcat容器组成

  StandardServer、StandardService、Connector、StandardContext这些容器,彼此之间都有父子关系,每个容器都可能包含零个或者多个子容器,这些子容器可能存在不同类型或者相同类型的多个,如图3所示。

图3  Tomcat容器组成

Tomcat容器状态

  目前,Tomcat的容器具有以下状态:
  • NEW:容器刚刚创建时,即在LifecycleBase实例构造完成时的状态。
  • INITIALIZED:容器初始化完成时的状态。
  • STARTING_PREP:容器启动前的状态。
  • STARTING:容器启动过程中的状态。
  • STARTED:容器启动完成的状态。
  • STOPPING_PREP:容器停止前的状态。
  • STOPPING:容器停止过程中的状态。
  • STOPPED:容器停止完成的状态。
  • DESTROYED:容器销毁后的状态。
  • FAILED:容器启动、停止过程中出现异常的状态。
  • MUST_STOP:此状态未使用。
  • MUST_DESTROY:此状态未使用。
这些状态都定义在枚举类LifecycleState中。

事件与监听

  每个容器由于继承自LifecycleBase,当容器状态发生变化时,都会调用fireLifecycleEvent方法,生成LifecycleEvent,并且交由此容器的事件监听器处理。LifecycleBase的fireLifecycleEvent方法的实现见代码清单1。
代码清单1
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * Allow sub classes to fire {@link Lifecycle} events. 
  3.  *  
  4.  * @param type  Event type 
  5.  * @param data  Data associated with event. 
  6.  */  
  7. protected void fireLifecycleEvent(String type, Object data) {  
  8.     lifecycle.fireLifecycleEvent(type, data);  
  9. }  
lifecycle的定义如下:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * Used to handle firing lifecycle events. 
  3.  * TODO: Consider merging LifecycleSupport into this class. 
  4.  */  
  5. private LifecycleSupport lifecycle = new LifecycleSupport(this);  
LifecycleSupport的fireLifecycleEvent方法的实现,见代码清单2。
代码清单2
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * Notify all lifecycle event listeners that a particular event has 
  3.  * occurred for this Container.  The default implementation performs 
  4.  * this notification synchronously using the calling thread. gja 
  5.  * 
  6.  * @param type Event type 
  7.  * @param data Event data 
  8.  */  
  9. public void fireLifecycleEvent(String type, Object data) {  
  10.   
  11.   
  12.     LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);  
  13.     LifecycleListener interested[] = listeners;  
  14.     for (int i = 0; i < interested.length; i++)  
  15.         interested[i].lifecycleEvent(event);  
  16.   
  17.   
  18. }  
代码清单2将事件通知给所有监听当前容器的生命周期监听器LifecycleListener,并调用LifecycleListener的lifecycleEvent方法。每个容器都维护这一个监听器缓存,其实现如下:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * The set of registered LifecycleListeners for event notifications. 
  3.  */  
  4. private LifecycleListener listeners[] = new LifecycleListener[0];  
那么listeners中的监听器是何时添加进来的呢?每个容器在新建、初始化、启动,销毁,被添加到父容器的过程中都会调用父类LifecycleBase的addLifecycleListener方法,addLifecycleListener的实现见代码清单3。
代码清单3
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. public void addLifecycleListener(LifecycleListener listener) {  
  3.     lifecycle.addLifecycleListener(listener);  
  4. }  
从代码清单3看到,LifecycleBase的addLifecycleListener方法实际是对LifecycleSupport的addLifecycleListener方法的简单代理,LifecycleSupport的addLifecycleListener方法的实现,见代码清单4。
 代码清单4
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * Add a lifecycle event listener to this component. 
  3.  * 
  4.  * @param listener The listener to add 
  5.  */  
  6. public void addLifecycleListener(LifecycleListener listener) {  
  7.   
  8.   
  9.   synchronized (listenersLock) {  
  10.       LifecycleListener results[] =  
  11.         new LifecycleListener[listeners.length + 1];  
  12.       for (int i = 0; i < listeners.length; i++)  
  13.           results[i] = listeners[i];  
  14.       results[listeners.length] = listener;  
  15.       listeners = results;  
  16.   }  
  17.   
  18.   
  19. }  
在代码清单2中,我们讲过容器会最终调用每个对此容器感兴趣的LifecycleListener的lifecycleEvent方法,那么LifecycleListener的lifecycleEvent方法会做些什么呢?为了简单起见,我们以监听器AprLifecycleListener为例,AprLifecycleListener的lifecycleEvent方法的实现,见代码清单5。
代码清单5
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * Primary entry point for startup and shutdown events. 
  3.  * 
  4.  * @param event The event that has occurred 
  5.  */  
  6. public void lifecycleEvent(LifecycleEvent event) {  
  7.   
  8.   
  9.     if (Lifecycle.INIT_EVENT.equals(event.getType())) {  
  10.         synchronized (lock) {  
  11.             init();  
  12.             if (aprAvailable) {  
  13.                 try {  
  14.                     initializeSSL();  
  15.                 } catch (Throwable t) {  
  16.                     log.info(sm.getString("aprListener.sslInit"));  
  17.                 }  
  18.             }  
  19.         }  
  20.     } else if (Lifecycle.AFTER_STOP_EVENT.equals(event.getType())) {  
  21.         synchronized (lock) {  
  22.             if (!aprAvailable) {  
  23.                 return;  
  24.             }  
  25.             try {  
  26.                 terminateAPR();  
  27.             } catch (Throwable t) {  
  28.                 log.info(sm.getString("aprListener.aprDestroy"));  
  29.             }  
  30.         }  
  31.     }  
  32.   
  33.   
  34. }  

容器生命周期

  每个容器都会有自身的生命周期,其中也涉及状态的迁移,以及伴随的事件生成,本节详细介绍Tomcat中的容器生命周期实现。所有容器的转态转换(如新疆、初始化、启动、停止等)都是由外到内,由上到下进行,即先执行父容器的状态转换及相关操作,然后再执行子容器的转态转换,这个过程是层层迭代执行的。

容器新建

  所有容器在构造的过程中,都会首先对父类LifecycleBase进行构造。LifecycleBase中定义了所有容器的起始状态为LifecycleState.NEW,代码如下:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * The current state of the source component. 
  3.  */  
  4. private volatile LifecycleState state = LifecycleState.NEW;  

容器初始化

  每个容器的init方法是自身初始化的入口,其初始化过程如图4所示。

图4  容器初始化时序图

  图4中所说的具体容器,实际就是LifecycleBase的具体实现类,目前LifecycleBase的类继承体系如图5所示。

图5  LifecycleBase的类继承体系

  根据图4所示的初始化过程,我们对Tomcat的源码进行分析,其处理步骤如下:
  1. 调用方调用容器父类LifecycleBase的init方法,LifecycleBase的init方法主要完成一些所有容器公共抽象出来的动作;
  2. LifecycleBase的init方法调用具体容器的initInternal方法实现,此initInternal方法用于对容器本身真正的初始化;
  3. 具体容器的initInternal方法调用父类LifecycleMBeanBase的initInternal方法实现,此initInternal方法用于将容器托管到JMX,便于运维管理;
  4. LifecycleMBeanBase的initInternal方法调用自身的register方法,将容器作为MBean注册到MBeanServer;
  5. 容器如果有子容器,会调用子容器的init方法;
  6. 容器初始化完毕,LifecycleBase会将容器的状态更改为初始化完毕,即LifecycleState.INITIALIZED。
  现在对容器初始化的源码进行分析,init方法的实现见代码清单6。

代码清单6
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public synchronized final void init() throws LifecycleException {  
  2.     if (!state.equals(LifecycleState.NEW)) {  
  3.         invalidTransition(Lifecycle.INIT_EVENT);  
  4.     }  
  5.   
  6.   
  7.     initInternal();  
  8.       
  9.     setState(LifecycleState.INITIALIZED);  
  10. }  

代码清单6说明,只有当前容器的状态处于LifecycleState.NEW的才可以被初始化,真正执行初始化的方法是initInternal,当初始化完毕,当前容器的状态会被更改为LifecycleState.INITIALIZED。为了简便起见,我们还是以StandardServer这个容器为例,StandardServer的initInternal方法的实现见代码清单7。

代码清单7
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. protected void initInternal() throws LifecycleException {  
  3.       
  4.     super.initInternal();  
  5.   
  6.   
  7.     // Register global String cache geng  
  8.     // Note although the cache is global, if there are multiple Servers  
  9.     // present in the JVM (may happen when embedding) then the same cache   
  10.     // will be registered under multiple names  
  11.     onameStringCache = register(new StringCache(), "type=StringCache");  
  12.   
  13.   
  14.     // Register the MBeanFactory  
  15.     onameMBeanFactory = register(new MBeanFactory(), "type=MBeanFactory");  
  16.       
  17.     // Register the naming resources  
  18.     onameNamingResoucres = register(globalNamingResources,  
  19.             "type=NamingResources");  
  20.       
  21.     // Initialize our defined Services  
  22.     for (int i = 0; i < services.length; i++) {  
  23.         services[i].init();  
  24.     }  
  25. }  

通过分析StandardServer的initInternal方法,其处理过程如下:

步骤一 将当前容器注册到JMX

  调用父类LifecycleBase的initInternal方法(见代码清单8),为当前容器创建DynamicMBean,并注册到JMX中。

 代码清单8
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. protected void initInternal() throws LifecycleException {  
  3.       
  4.     // If oname is not null then registration has already happened via jiaan  
  5.     // preRegister().  
  6.     if (oname == null) {  
  7.         mserver = Registry.getRegistry(nullnull).getMBeanServer();  
  8.           
  9.         oname = register(this, getObjectNameKeyProperties());  
  10.     }  
  11. }  

StandardServer实现的getObjectNameKeyProperties方法如下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. protected final String getObjectNameKeyProperties() {  
  3.     return "type=Server";  
  4. }  

LifecycleBase的register方法(见代码清单9)会为当前容器创建对应的注册名称,以StandardServer为例,getDomain默认返回Catalina,因此StandardServer的JMX注册名称默认为Catalina:type=Server,真正的注册在registerComponent方法中实现。

代码清单9
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. protected final ObjectName register(Object obj,  
  2.         String objectNameKeyProperties) {  
  3.       
  4.     // Construct an object name with the right domain  
  5.     StringBuilder name = new StringBuilder(getDomain());  
  6.     name.append(':');  
  7.     name.append(objectNameKeyProperties);  
  8.   
  9.   
  10.     ObjectName on = null;  
  11.   
  12.   
  13.     try {  
  14.         on = new ObjectName(name.toString());  
  15.           
  16.         Registry.getRegistry(nullnull).registerComponent(obj, on, null);  
  17.     } catch (MalformedObjectNameException e) {  
  18.         log.warn(sm.getString("lifecycleMBeanBase.registerFail", obj, name),  
  19.                 e);  
  20.     } catch (Exception e) {  
  21.         log.warn(sm.getString("lifecycleMBeanBase.registerFail", obj, name),  
  22.                 e);  
  23.     }  
  24.   
  25.   
  26.     return on;  
  27. }  

Registry的registerComponent方法会为当前容器(如StandardServer)创建DynamicMBean,并且注册到MBeanServer,见代码清单10。

代码清单10
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** Register a component  
  2.  * XXX make it private  
  3.  *  
  4.  * @param bean 
  5.  * @param oname 
  6.  * @param type 
  7.  * @throws Exception 
  8.  */   
  9. public void registerComponent(Object bean, ObjectName oname, String type)  
  10.        throws Exception  
  11. {  
  12.     if( log.isDebugEnabled() ) {  
  13.         log.debug( "Managed= "+ oname);  
  14.     }  
  15.   
  16.   
  17.     if( bean ==null ) {  
  18.         log.error("Null component " + oname );  
  19.         return;  
  20.     }  
  21.   
  22.   
  23.     try {  
  24.         if( type==null ) {  
  25.             type=bean.getClass().getName();  
  26.         }  
  27.   
  28.   
  29.         ManagedBean managed = findManagedBean(bean.getClass(), type);  
  30.   
  31.   
  32.         // The real mbean is created and registered  
  33.         DynamicMBean mbean = managed.createMBean(bean);  
  34.   
  35.   
  36.         if(  getMBeanServer().isRegistered( oname )) {  
  37.             if( log.isDebugEnabled()) {  
  38.                 log.debug("Unregistering existing component " + oname );  
  39.             }  
  40.             getMBeanServer().unregisterMBean( oname );  
  41.         }  
  42.   
  43.   
  44.         getMBeanServer().registerMBean( mbean, oname);  
  45.     } catch( Exception ex) {  
  46.         log.error("Error registering " + oname, ex );  
  47.         throw ex;  
  48.     }  
  49. }  

步骤二 将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的源码进行分析,其处理步骤如下:
  1. 调用方调用容器父类LifecycleBase的start方法,LifecycleBase的start方法主要完成一些所有容器公共抽象出来的动作;
  2. LifecycleBase的start方法先将容器状态改为LifecycleState.STARTING_PREP,然后调用具体容器的startInternal方法实现,此startInternal方法用于对容器本身真正的初始化;
  3. 具体容器的startInternal方法会将容器状态改为LifecycleState.STARTING,容器如果有子容器,会调用子容器的start方法启动子容器;
  4. 容器启动完毕,LifecycleBase会将容器的状态更改为启动完毕,即LifecycleState.STARTED。
  现在对容器启动的源码进行分析,start方法的实现见代码清单11。

代码清单11
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. public synchronized final void start() throws LifecycleException {  
  3.       
  4.     if (LifecycleState.STARTING_PREP.equals(state) ||  
  5.             LifecycleState.STARTING.equals(state) ||  
  6.             LifecycleState.STARTED.equals(state)) {  
  7.           
  8.         if (log.isDebugEnabled()) {  
  9.             Exception e = new LifecycleException();  
  10.             log.debug(sm.getString("lifecycleBase.alreadyStarted",  
  11.                     toString()), e);  
  12.         } else if (log.isInfoEnabled()) {  
  13.             log.info(sm.getString("lifecycleBase.alreadyStarted",  
  14.                     toString()));  
  15.         }  
  16.           
  17.         return;  
  18.     }  
  19.       
  20.     if (state.equals(LifecycleState.NEW)) {  
  21.         init();  
  22.     } else if (!state.equals(LifecycleState.INITIALIZED) &&  
  23.             !state.equals(LifecycleState.STOPPED)) {  
  24.         invalidTransition(Lifecycle.BEFORE_START_EVENT);  
  25.     }  
  26.   
  27.   
  28.     setState(LifecycleState.STARTING_PREP);  
  29.   
  30.   
  31.     try {  
  32.         startInternal();  
  33.     } catch (LifecycleException e) {  
  34.         setState(LifecycleState.FAILED);  
  35.         throw e;  
  36.     }  
  37.   
  38.   
  39.     if (state.equals(LifecycleState.FAILED) ||  
  40.             state.equals(LifecycleState.MUST_STOP)) {  
  41.         stop();  
  42.     } else {  
  43.         // Shouldn't be necessary but acts as a check that sub-classes are  
  44.         // doing what they are supposed to.  
  45.         if (!state.equals(LifecycleState.STARTING)) {  
  46.             invalidTransition(Lifecycle.AFTER_START_EVENT);  
  47.         }  
  48.           
  49.         setState(LifecycleState.STARTED);  
  50.     }  
  51. }  

代码清单11说明在真正启动容器之前需要做2种检查:
  1. 如果当前容器已经处于启动过程(即容器状态为LifecycleState.STARTING_PREP、LifecycleState.STARTING、LifecycleState.STARTED)中,则会产生并且用日志记录LifecycleException异常并退出。
  2. 如果容器依然处于LifecycleState.NEW状态,则在启动之前,首先确保初始化完毕。
代码清单11还说明启动容器完毕后,需要做1种检查,即如果容器启动异常导致容器进入LifecycleState.FAILED或者LifecycleState.MUST_STOP状态,则需要调用stop方法停止容器。
  现在我们重点分析startInternal方法,还是以StandardServer为例,其startInternal的实现见代码清单12所示。

代码清单12
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. protected void startInternal() throws LifecycleException {  
  3.   
  4.   
  5.     fireLifecycleEvent(CONFIGURE_START_EVENT, null);  
  6.     setState(LifecycleState.STARTING);  
  7.   
  8.   
  9.     // Start our defined Services  
  10.     synchronized (services) {  
  11.         for (int i = 0; i < services.length; i++) {  
  12.             services[i].start();  
  13.         }  
  14.     }  
  15. }  

  从代码清单12看到StandardServer的启动由以下步骤组成:
  1. 产生CONFIGURE_START_EVENT事件;
  2. 将自身状态更改为LifecycleState.STARTING;
  3. 调用子容器Service(默认为StandardService)的start方法启动子容器。
除了初始化、启动外,各个容器还有停止和销毁的生命周期,其原理与初始化、启动类似,本文不再赘述,有兴趣的读者可以自行研究。

  Tomcat启动完毕后,打开Java visualVM,打开Tomcat进程监控,给visualVM安装MBeans插件后,选择MBeans标签页可以对Tomcat所有注册到JMX中的对象进行管理,比如StandardService就向JMX暴露了start和stop等方法,这样管理员就可以动态管理Tomcat,如图7所示。

图7  使用JMX动态管理Tomcat

总结

  Tomcat通过将内部所有组件都抽象为容器,为容器提供统一的生命周期管理,各个子容器只需要关心各自的具体实现,这便于Tomcat以后扩展更多的容器,对于研究或者学习Tomcat的人来说,其设计清晰易懂。
1 0
原创粉丝点击