zoukankan      html  css  js  c++  java
  • Tomcat源码分析——生命周期管理

    前言

      从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

        /**
         * 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的源码进行分析,其处理步骤如下:

    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

        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的源码进行分析,其处理步骤如下:

    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

        @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种检查:

    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

        @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的启动由以下步骤组成:

    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的人来说,其设计清晰易懂。

    如需转载,请标明本文作者及出处——作者:jiaan.gja,本文原创首发:博客园,原文链接:http://www.cnblogs.com/jiaan-geng/p/4864501.html
  • 相关阅读:
    67. Add Binary
    66. Plus One
    64. Minimum Path Sum
    63. Unique Paths II
    How to skip all the wizard pages and go directly to the installation process?
    Inno Setup打包之先卸载再安装
    How to change the header background color of a QTableView
    Openstack object list 一次最多有一万个 object
    Openstack 的 Log 在 /var/log/syslog 里 【Ubuntu】
    Git 分支
  • 原文地址:https://www.cnblogs.com/jiaan-geng/p/4864501.html
Copyright © 2011-2022 走看看