文档章节

Tomcat源码学习(六)--Tomcat_7.0.70 生命周期管理

火龙战士
 火龙战士
发布于 2016/07/18 17:59
字数 3446
阅读 65
收藏 3

想必大家都知道,从server.xml文件解析出来的各个对象都是容器,比如:Server、Service、Connector等。这些容器都具有新建、初始化完成、启动、停止、失败、销毁等状态。Tomcat的实现机制是通过实现org.apache.catalina.Lifecycle接口来管理。

Tomcat--Lifecycle接口

定义了容器生命周期、容器状态转换及容器状态迁移事件的监听器注册和移除等主要接口。代码清单:

	public interface Lifecycle {

    public static final String BEFORE_INIT_EVENT = "before_init";

    public static final String AFTER_INIT_EVENT = "after_init";

    public static final String START_EVENT = "start";

    public static final String BEFORE_START_EVENT = "before_start";

    public static final String AFTER_START_EVENT = "after_start";

    public static final String STOP_EVENT = "stop";

    public static final String BEFORE_STOP_EVENT = "before_stop";

    public static final String AFTER_STOP_EVENT = "after_stop";

    public static final String AFTER_DESTROY_EVENT = "after_destroy";

    public static final String BEFORE_DESTROY_EVENT = "before_destroy";

    public static final String PERIODIC_EVENT = "periodic";

    public static final String CONFIGURE_START_EVENT = "configure_start";

    public static final String CONFIGURE_STOP_EVENT = "configure_stop";

    public void addLifecycleListener(LifecycleListener listener);

    public LifecycleListener[] findLifecycleListeners();

    public void removeLifecycleListener(LifecycleListener listener);

    public void init() throws LifecycleException;

    public void start() throws LifecycleException;

    public void stop() throws LifecycleException;

    public void destroy() throws LifecycleException;

    public LifecycleState getState();

    public String getStateName();

    public interface SingleUse {
    }
}

其中,最重要的方法时start和stop方法。父组件通过这两个方法来启动/关闭该组件。addLifecycleListener,findLifecycleListeners,removeLifecycleListener三个方法用于向组件注册/查找/删除监听器。当事件发生时,会触发监听器。接口中还定义了相关事件。

下面从一幅图来了解Tomcat涉及生命周期管理的主要类:

  • 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?

JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。

JMX体系结构分为以下四个层次:

设备层

设备层(Instrumentation Level):主要定义了信息模型。在JMX中,各种管理对象以管理构件的形式存在,需要管理时,向MBean服务器进行注册。该层还定义了通知机制以及一些辅助元数据类。

代理层

代理层(Agent Level):主要定义了各种服务以及通信模型。该层的核心是一个MBean服务器,所有的管理构件都需要向它注册,才能被管理。注册在MBean服务器上管理构件并不直接和远程应用程序进行通信,它们通过协议适配器和连接器进行通信。而协议适配器和连接器也以管理构件的形式向MBean服务器注册才能提供相应的服务。

分布服务层

分布服务层(Distributed Service Level):主要定义了能对代理层进行操作的管理接口和构件,这样管理者就可以操作代理。然而,当前的JMX规范并没有给出这一层的具体规范。

附加管理协议API

定义的API主要用来支持当前已经存在的网络管理协议,如SNMP、TMN、CIM/WBEM等。

Tomcat--事件、监听

每个容器由于继承自LifecycleBase,当容器状态发生变化时,都会调用fireLifecycleEvent方法,生成LifecycleEvent,并且交由此容器的事件监听器处理。

LifecycleBase的fireLifecycleEvent方法的实现:

protected void fireLifecycleEvent(String type, Object data) {
    lifecycle.fireLifecycleEvent(type, data);
}


//lifecycle定义
private LifecycleSupport lifecycle = new LifecycleSupport(this);


//LifecycleSupport的fireLifecycleEvent方法的实现
  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);

}

然后将事件通知给所有监听当前容器的生命周期监听器LifecycleListener,并调用LifecycleListener的lifecycleEvent方法。

那么监听器LifecycleListener是何时注册进来的?其实每个容器在新建、初始化、启动,销毁,被添加到父容器的过程中都会调用父类LifecycleBase的addLifecycleListener方法:

 public void addLifecycleListener(LifecycleListener listener) {
    lifecycle.addLifecycleListener(listener);
}

LifecycleBase的addLifecycleListener方法实际是对LifecycleSupport的addLifecycleListener方法的简单代理,LifecycleSupport的addLifecycleListener方法的实现:

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;
  }

}

容器会最终调用每个对此容器感兴趣的LifecycleListener的lifecycleEvent方法,那么LifecycleListener的lifecycleEvent方法会做些什么呢?为了简单起见,我们以监听器JasperListener为例,JasperListener的lifecycleEvent方法的实现:

  public void lifecycleEvent(LifecycleEvent event) {

    if (Lifecycle.BEFORE_INIT_EVENT.equals(event.getType())) {
        try {
            // Set JSP factory
            Class.forName("org.apache.jasper.compiler.JspRuntimeContext",
                          true,
                          this.getClass().getClassLoader());
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            // Should not occur, obviously
            log.warn("Couldn't initialize Jasper", t);
        }
        // Another possibility is to do directly:
        // JspFactory.setDefaultFactory(new JspFactoryImpl());
    }

}

Tomcat--容器生命周期

StandardServer、StandardService、Connector、StandardContext这些容器,彼此之间都有父子关系,每个容器都可能包含零个或者多个子容器,这些子容器可能存在不同类型或者相同类型的多个。在一个容器创建成功后,会有以下状态:

  • NEW:容器刚刚创建时,即在LifecycleBase实例构造完成时的状态。

  • INITIALIZED:容器初始化完成时的状态。

  • STARTING_PREP:容器启动前的状态。

  • STARTING:容器启动过程中的状态。

  • STARTED:容器启动完成的状态。

  • STOPPING_PREP:容器停止前的状态。

  • STOPPING:容器停止过程中的状态。

  • STOPPED:容器停止完成的状态。

  • DESTROYED:容器销毁后的状态。

  • FAILED:容器启动、停止过程中出现异常的状态。

  • MUST_STOP:此状态未使用。

  • MUST_DESTROY:此状态未使用。

这些状态都定义在枚举类LifecycleState中。代码详单:

public enum LifecycleState {
    NEW(false, null),
    INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT),
    INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT),
    STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT),
    STARTING(true, Lifecycle.START_EVENT),
    STARTED(true, Lifecycle.AFTER_START_EVENT),
    STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
    STOPPING(false, Lifecycle.STOP_EVENT),
    STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
    DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
    DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
    FAILED(false, null),
    /**
     * @deprecated Unused. Will be removed in Tomcat 9.0.x. The state transition
     *             checking in {[@link](http://my.oschina.net/u/393) org.apache.catalina.util.LifecycleBase}
     *             makes it impossible to use this state. The intended behaviour
     *             can be obtained by setting the state to
     *             {[@link](http://my.oschina.net/u/393) LifecycleState#FAILED} in
     *             <code>LifecycleBase.startInternal()</code>
     */
    @Deprecated
    MUST_STOP(true, null),
    /**
     * @deprecated Unused. Will be removed in Tomcat 9.0.x. The state transition
     *             checking in {@link org.apache.catalina.util.LifecycleBase}
     *             makes it impossible to use this state. The intended behaviour
     *             can be obtained by implementing {@link Lifecycle.SingleUse}.
     */
    @Deprecated
    MUST_DESTROY(false, null);

    private final boolean available;
    private final String lifecycleEvent;

    private LifecycleState(boolean available, String lifecycleEvent) {
        this.available = available;
        this.lifecycleEvent = lifecycleEvent;
    }

    /**
     * May the public methods other than property getters/setters and lifecycle
     * methods be called for a component in this state? It returns
     * <code>true</code> for any component in any of the following states:
     * <ul>
     * <li>{@link #STARTING}</li>
     * <li>{@link #STARTED}</li>
     * <li>{@link #STOPPING_PREP}</li>
     * <li>{@link #MUST_STOP}</li>
     * </ul>
     */
    public boolean isAvailable() {
        return available;
    }

    /**
     *
     */
    public String getLifecycleEvent() {
        return lifecycleEvent;
    }
}

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

Tomcat容器生命周期----新建

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

private volatile LifecycleState state = LifecycleState.NEW;

Tomcat容器生命周期----初始化

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

  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方法的实现

     public final synchronized void init() throws LifecycleException {
     if (!state.equals(LifecycleState.NEW)) {
         invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
     }
     setStateInternal(LifecycleState.INITIALIZING, null, false);
    
     try {
         initInternal();//调用具体容器的initInternal方法实现
     } catch (Throwable t) {
         ExceptionUtils.handleThrowable(t);
         setStateInternal(LifecycleState.FAILED, null, false);
         throw new LifecycleException(
                 sm.getString("lifecycleBase.initFail",toString()), t);
     }
    
     setStateInternal(LifecycleState.INITIALIZED, null, false);
     }
    

只有当前容器的状态处于LifecycleState.NEW的才可以被初始化,真正执行初始化的方法是initInternal,当初始化完毕,当前容器的状态会被更改为LifecycleState.INITIALIZED。以StandardService这个容器为例举例分析,StandardService容器的initInternal方法实现:

  protected void initInternal() throws LifecycleException {

    super.initInternal();
    
    if (container != null) {
        container.init();
    }

    // Initialize any Executors
    for (Executor executor : findExecutors()) {
        if (executor instanceof LifecycleMBeanBase) {
            ((LifecycleMBeanBase) executor).setDomain(getDomain());
        }
        executor.init();
    }

    // Initialize our defined Connectors
    synchronized (connectorsLock) {
        for (Connector connector : connectors) {
            try {
                connector.init();
            } catch (Exception e) {
                String message = sm.getString(
                        "standardService.connector.initFailed", connector);
                log.error(message, e);

                if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                    throw new LifecycleException(message);
            }
        }
    }
}

其处理过程:

a、调用父类LifecycleBase的initInternal方法,为当前容器创建DynamicMBean,并注册到JMX中。

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());
    }
}

//getObjectNameKeyProperties()方法
public final String getObjectNameKeyProperties() {
    return "type=Service";
}

LifecycleBase的register方法会为当前容器创建对应的注册名称,以StandardService为例,getDomain默认返回Catalina,因此StandardService的JMX注册名称默认为Catalina:type=Service,真正的注册在registerComponent方法中实现。

//register方法
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;
}

//registerComponent方法
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;
    }
}

Registry的registerComponent方法会为当前容器(如StandardService)创建DynamicMBean,并且注册到MBeanServer中。

b、将StringCache、MBeanFactory、globalNamingResources注册到JMX

其中StringCache的注册名为Catalina:type=StringCache,MBeanFactory的注册名为Catalina:type=MBeanFactory,globalNamingResources的注册名为Catalina:type=NamingResources(如StandardService则为:Catalina:type=Service)

c、初始化子容器

主要对Service子容器进行初始化,默认是StandardService。

注意:个别容器并不完全遵循以上的初始化过程,比如ProtocolHandler作为Connector的子容器,其初始化过程并不是由Connector的initInternal方法调用的,而是与启动过程一道被Connector的startInternal方法所调用。

Tomcat容器生命周期----容器启动

每个容器的start方法是自身启动的入口

  1. 调用方调用容器父类LifecycleBase的start方法,LifecycleBase的start方法主要完成一些所有容器公共抽象出来的动作;

  2. LifecycleBase的start方法先将容器状态改为LifecycleState.STARTING_PREP,然后调用具体容器的startInternal方法实现,此startInternal方法用于对容器本身真正的初始化;

  3. 具体容器的startInternal方法会将容器状态改为LifecycleState.STARTING,容器如果有子容器,会调用子容器的start方法启动子容器;

  4. 容器启动完毕,LifecycleBase会将容器的状态更改为启动完毕,即LifecycleState.STARTED。

     //LifecycleBase的start方法
     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);
         }
    
         setStateInternal(LifecycleState.STARTING_PREP, null, false);
    
         try {
             startInternal();
         } 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);
         }
    
         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);
         }
     }
    

在真正启动容器之前需要做2种检查:

如果当前容器已经处于启动过程(即容器状态为LifecycleState.STARTING_PREP、LifecycleState.STARTING、LifecycleState.STARTED)中,则会产生并且用日志记录LifecycleException异常并退出。 如果容器依然处于LifecycleState.NEW状态,则在启动之前,首先确保初始化完毕。

启动容器完毕后,需要做1种检查: 即如果容器启动异常导致容器进入LifecycleState.FAILED或者LifecycleState.MUST_STOP状态,则需要调用stop方法停止容器。

以StandardService为例,其startInternal的实现:

protected void startInternal() throws LifecycleException {

    if(log.isInfoEnabled())
        log.info(sm.getString("standardService.start.name", this.name));
    setState(LifecycleState.STARTING);//将自身状态更改为LifecycleState.STARTING;

    // Start our defined Container first
    if (container != null) {
        synchronized (container) {
            container.start();//调用子容器Service的start方法启动子容器。
        }
    }

    synchronized (executors) {
        for (Executor executor: executors) {
            executor.start();
        }
    }

    // Start our defined Connectors second
    synchronized (connectorsLock) {
        for (Connector connector: connectors) {
            try {
                // If it has already failed, don't try and start it
                if (connector.getState() != LifecycleState.FAILED) {
                    connector.start();
                }
            } catch (Exception e) {
                log.error(sm.getString(
                        "standardService.connector.startFailed",
                        connector), e);
            }
        }
    }
}

除了初始化、启动外,各个容器还有停止和销毁的生命周期,其原理与初始化、启动类似。

总结

Tomcat通过将内部所有组件都抽象为容器,为容器提供统一的生命周期管理,各个子容器只需要关心各自的具体实现,这便于Tomcat以后扩展更多的容器。

© 著作权归作者所有

火龙战士

火龙战士

粉丝 119
博文 138
码字总数 101234
作品 0
北京
后端工程师
私信 提问
tomcat 启动报错

新手求教。以前建的web项目文件夹,刚刚整理,删除了一些过去的servlet文件。之后发现之前可以正常显示的jsp页面都报404了。注意是所有的jsp页面。然后我发现tomcat启动也报错 八月 18, 2016...

加冕为王
2016/08/18
728
2
线上环境跑数据接口出现org.springframework.boot.web.support.ErrorPageFilter

20:24:52.905 [http-bio-882-exec-4400] ERROR org.springframework.boot.web.support.ErrorPageFilter - Forwarding to error page from request [/V24] due to exception [Could not parse......

我要五个字
2017/05/26
3.8K
0
centos7设置tomcat7为系统服务的方法

A.在/usr/lib/systemd/system/目录下新建文件tomcat7.service,内容如下: [Unit] Description=Tomcat7 After=syslog.target network.target remote-fs.target nss-lookup.target [Service]......

有功夫
2018/05/30
0
0
Java Web开发入门 - 第3章 Tomcat

Tomcat安装与运行 Web服务器完成底层的网络处理,包括HTTP协议报文格式的编解码、管理具体web请求处理线程等操作。 Tomcat目前最流行最常见的基于Java的web应用服务器软件。 Tomcat Apache ...

抢小孩糖吃
2016/08/19
177
0
本地发布新pitaya流程

将production副本里的 jdbc文件和system文件替换掉新的pitaya里的 将刚替换好的pitaya文件到tomcat里替换掉旧的pitaya文件 上面方法是旧的新方法E:workspacePitaya-webpitaya 在此路径下按住...

LM加强
2016/10/28
10
0

没有更多内容

加载失败,请刷新页面

加载更多

MainThreadSupport

MainThreadSupport EventBus 3.0 中的代码片段. org.greenrobot.eventbus.MainThreadSupport 定义一个接口,并给出默认实现类. 调用者可以在EventBus的构建者中替换该实现. public interface ...

马湖村第九后羿
32分钟前
3
0
指定要使用的形状来代替文字的显示

控制手机键盘弹出的功能只能在ios上实现,安卓是实现不了的,所以安卓只能使用type类型来控制键盘类型,例如你要弹出数字键盘就使用type="number",如果要弹出电话键盘就使用type="tel",但这...

前端老手
42分钟前
5
0
总结:Raft协议

一、Raft协议是什么? 分布式一致性算法。即解决分布式系统中各个副本数据一致性问题。 二、Raft的日志广播过程 发送日志到所有Followers(Raft中将非Leader节点称为Follower)。 Followers收...

浮躁的码农
49分钟前
7
0
Flask-admin Model View字段介绍

Model View字段介绍 can_create = True 是否可以创建can_edit = True 是否可以编辑can_delete = True 是否可以删除list_template = 'admin/model/list.html' 修改显......

dillonxiao
今天
5
0
从AnnotationTransactionAspect开始rushSpring事务

0. Spring 事务 with LTW 0.1. Spring 事务 With LTW的原因: Pure Proxy-base mode有缺陷,其失效原因分析及使用方法及运行机制(LoadTimeWeaverBeanDefinitionParser和 AspectJWeavingEnable......

Aruforce
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部