TOMCAT源码分析——生命周期管理(二)

简介:

前言

  我在《TOMCAT源码分析——生命周期管理(一)》一文中介绍了TOMCAT生命周期类接口设计、JMX、容器以及基于容器的事件与监听等内容,本文将接着介绍Tomcat7.0中容器生命周期的管理。

容器生命周期

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

容器新建

  所有容器在构造的过程中,都会首先对父类LifecycleBase进行构造。LifecycleBase中定义了所有容器的起始状态为LifecycleState.NEW,代码如下:

    /**
     * The current state of the source component.
     */
    private volatile LifecycleState state = LifecycleState.NEW;

容器初始化

  每个容器的init方法是自身初始化的入口,其初始化过程如图4所示。
Tomcat_
图4  容器初始化时序图
图4中所说的具体容器,实际就是LifecycleBase的具体实现类,目前LifecycleBase的类继承体系如图5所示。
LifecycleBase_
图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所示。
Tomcat_
图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所示。
JMX_
图7  使用JMX动态管理Tomcat

总结

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

相关文章
|
2月前
|
监控 网络协议 应用服务中间件
【Tomcat源码分析】从零开始理解 HTTP 请求处理 (第一篇)
本文详细解析了Tomcat架构中复杂的`Connector`组件。作为客户端与服务器间沟通的桥梁,`Connector`负责接收请求、封装为`Request`和`Response`对象,并传递给`Container`处理。文章通过四个关键问题逐步剖析了`Connector`的工作原理,并深入探讨了其构造方法、`init()`与`start()`方法。通过分析`ProtocolHandler`、`Endpoint`等核心组件,揭示了`Connector`初始化及启动的全过程。本文适合希望深入了解Tomcat内部机制的读者。欢迎关注并点赞,持续更新中。如有问题,可搜索【码上遇见你】交流。
【Tomcat源码分析】从零开始理解 HTTP 请求处理 (第一篇)
|
2月前
|
人工智能 前端开发 Java
【Tomcat源码分析】启动过程深度解析 (二)
本文深入探讨了Tomcat启动Web应用的过程,重点解析了其加载ServletContextListener及Servlet的机制。文章从Bootstrap反射调用Catalina的start方法开始,逐步介绍了StandardServer、StandardService、StandardEngine、StandardHost、StandardContext和StandardWrapper的启动流程。每个组件通过Lifecycle接口协调启动,子容器逐层启动,直至整个服务器完全启动。此外,还详细分析了Pipeline及其Valve组件的作用,展示了Tomcat内部组件间的协作机制。
【Tomcat源码分析】启动过程深度解析 (二)
|
2月前
|
前端开发 Java 应用服务中间件
【Tomcat源码分析 】"深入探索:Tomcat 类加载机制揭秘"
本文详细介绍了Java类加载机制及其在Tomcat中的应用。首先回顾了Java默认的类加载器,包括启动类加载器、扩展类加载器和应用程序类加载器,并解释了双亲委派模型的工作原理及其重要性。接着,文章分析了Tomcat为何不能使用默认类加载机制,因为它需要解决多个应用程序共存时的类库版本冲突、资源共享、类库隔离及JSP文件热更新等问题。最后,详细展示了Tomcat独特的类加载器设计,包括Common、Catalina、Shared、WebApp和Jsp类加载器,确保了系统的稳定性和安全性。通过这种设计,Tomcat实现了不同应用程序间的类库隔离与共享,同时支持JSP文件的热插拔。
【Tomcat源码分析 】"深入探索:Tomcat 类加载机制揭秘"
|
2月前
|
设计模式 应用服务中间件 容器
【Tomcat源码分析】Pipeline 与 Valve 的秘密花园
本文深入剖析了Tomcat中的Pipeline和Valve组件。Valve作为请求处理链中的核心组件,通过接口定义了关键方法;ValveBase为其基类,提供了通用实现。Pipeline则作为Valve容器,通过首尾相连的Valve链完成业务处理。StandardPipeline实现了Pipeline接口,提供了详细的Valve管理逻辑。通过对代码的详细分析,揭示了模板方法模式和责任链模式的应用,展示了系统的扩展性和模块化设计。
【Tomcat源码分析】Pipeline 与 Valve 的秘密花园
|
2月前
|
设计模式 人工智能 安全
【Tomcat源码分析】生命周期机制 Lifecycle
Tomcat内部通过各种组件协同工作,构建了一个复杂的Web服务器架构。其中,`Lifecycle`机制作为核心,管理组件从创建到销毁的整个生命周期。本文详细解析了Lifecycle的工作原理及其方法,如初始化、启动、停止和销毁等关键步骤,并展示了LifecycleBase类如何通过状态机和模板模式实现这一过程。通过深入理解Lifecycle,我们可以更好地掌握组件生命周期管理,提升系统设计能力。欢迎关注【码上遇见你】获取更多信息,或搜索【AI贝塔】体验免费的Chat GPT。希望本章内容对你有所帮助。
|
3月前
|
网络协议 Java 应用服务中间件
Tomcat源码分析 (一)----- 手撕Java Web服务器需要准备哪些工作
本文探讨了后端开发中Web服务器的重要性,特别是Tomcat框架的地位与作用。通过解析Tomcat的内部机制,文章引导读者理解其复杂性,并提出了一种实践方式——手工构建简易Web服务器,以此加深对Web服务器运作原理的认识。文章还详细介绍了HTTP协议的工作流程,包括请求与响应的具体格式,并通过Socket编程在Java中的应用实例,展示了客户端与服务器间的数据交换过程。最后,通过一个简单的Java Web服务器实现案例,说明了如何处理HTTP请求及响应,强调虽然构建基本的Web服务器相对直接,但诸如Tomcat这样的成熟框架提供了更为丰富和必要的功能。
|
6月前
|
前端开发 Java 应用服务中间件
|
6月前
|
XML Java 应用服务中间件
SpringBoot配置外部Tomcat项目启动流程源码分析(长文)
SpringBoot配置外部Tomcat项目启动流程源码分析(长文)
511 0
|
存储 缓存 前端开发
07.Tomcat源码分析——类加载体系
由于在生产环境中,Tomcat一般部署在Linux系统下,所以本文将以 startup.sh shell脚本为准,对Tomcat的启动进行分析。
62 0
07.Tomcat源码分析——类加载体系