版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/beliefer/article/details/51473807
前言
从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;
什么是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:此状态未使用。
事件与监听
每个容器由于继承自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 容器初始化时序图
图5 LifecycleBase的类继承体系
根据图4所示的初始化过程,我们对Tomcat的源码进行分析,其处理步骤如下:
- 调用方调用容器父类LifecycleBase的init方法,LifecycleBase的init方法主要完成一些所有容器公共抽象出来的动作;
- LifecycleBase的init方法调用具体容器的initInternal方法实现,此initInternal方法用于对容器本身真正的初始化;
- 具体容器的initInternal方法调用父类LifecycleMBeanBase的initInternal方法实现,此initInternal方法用于将容器托管到JMX,便于运维管理;
- LifecycleMBeanBase的initInternal方法调用自身的register方法,将容器作为MBean注册到MBeanServer;
- 容器如果有子容器,会调用子容器的init方法;
- 容器初始化完毕,LifecycleBase会将容器的状态更改为初始化完毕,即LifecycleState.INITIALIZED。
代码清单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的源码进行分析,其处理步骤如下:
- 调用方调用容器父类LifecycleBase的start方法,LifecycleBase的start方法主要完成一些所有容器公共抽象出来的动作;
- LifecycleBase的start方法先将容器状态改为LifecycleState.STARTING_PREP,然后调用具体容器的startInternal方法实现,此startInternal方法用于对容器本身真正的初始化;
- 具体容器的startInternal方法会将容器状态改为LifecycleState.STARTING,容器如果有子容器,会调用子容器的start方法启动子容器;
- 容器启动完毕,LifecycleBase会将容器的状态更改为启动完毕,即LifecycleState.STARTED。
代码清单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种检查:
- 如果当前容器已经处于启动过程(即容器状态为LifecycleState.STARTING_PREP、LifecycleState.STARTING、LifecycleState.STARTED)中,则会产生并且用日志记录LifecycleException异常并退出。
- 如果容器依然处于LifecycleState.NEW状态,则在启动之前,首先确保初始化完毕。
现在我们重点分析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的启动由以下步骤组成:
- 产生CONFIGURE_START_EVENT事件;
- 将自身状态更改为LifecycleState.STARTING;
- 调用子容器Service(默认为StandardService)的start方法启动子容器。
Tomcat启动完毕后,打开Java visualVM,打开Tomcat进程监控,给visualVM安装MBeans插件后,选择MBeans标签页可以对Tomcat所有注册到JMX中的对象进行管理,比如StandardService就向JMX暴露了start和stop等方法,这样管理员就可以动态管理Tomcat,如图7所示。
图7 使用JMX动态管理Tomcat
总结
Tomcat通过将内部所有组件都抽象为容器,为容器提供统一的生命周期管理,各个子容器只需要关心各自的具体实现,这便于Tomcat以后扩展更多的容器,对于研究或者学习Tomcat的人来说,其设计清晰易懂。
后记:个人总结整理的《深入理解Spark:核心思想与源码分析》一书现在已经正式出版上市,目前京东、当当、天猫等网站均有销售,欢迎感兴趣的同学购买。
京东:http://item.jd.com/11846120.html
当当:http://product.dangdang.com/23838168.html