tomcat源码分析—生命周期及容器初始化过程
Tomcat服务器是由一系列可配置的组件构成的,其中核心组件是Catalina Servlet,它是最顶层组件。
Tomcat的各组件是在server.xml(CATALINA_HOME\conf\server.xml)配置的 ,tomcat启动是会读取server.xml,并且从中解析出一些对象,下面是一个不太完整的server.xml配置文件
<Server port="8006" shutdown="SHUTDOWN">
<!-- Security listener. Documentation at /docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener" /> -->
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="catelina">
<!--The connectors can use a shared executor, you can define one or more named thread pools -->
<!-- <Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="4"/> -->
<!-- A "Connector" using the shared thread pool -->
<!-- <Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> -->
<Connector port="8080" redirectPort="443" acceptCount="100" connectionTimeout="2000" />
<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" keystoreFile="/usr/certs/keystore.pfx" keystoreType="PKCS12"
keystorePass="21413631"/>
<Connector port="8009" enableLookups="false" redirectPort="443" protocol="AJP/1.3" />
<Engine name="catelina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/></Realm>
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Context path="" docBase="web" debug="0" reloadable="true"/>
</Host>
<Host name="www.qq158.com" appBase="/usr/local/tomcatdata/webapps" unpackWARs="true" autoDeploy="true">
<Context path="" docBase="wapi" debug="0" reloadable="true"/>
<Valve className="org.apache.catalina.valves.AccessLogValve" fileDateFormat="yyyy-MM-dd" pattern="%h %l %u %t %r %s %b %{Referer}i %{User-Agent}i %{x-jphone-uid}i %{x-up-subno}i" prefix="localhost_access_log." suffix=".txt" directory="/xvdb/logs/logwapi" condition="drop"/>
</Host>
</Engine>
</Service>
</Server>
可以看出tomcat的一些对象,比如 Server,Listener,Service,Connector,Engine ,Realm,Host,Context,Value等,
其中有很多对象是容器,比如Server、Service、Connector等,这些容器都具有新建,初始化,启动(start),停止(stop),销毁(destroy)等状态,
TOMCAT生命周期类接口设计
下图是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进行管理。
java管理程序扩展(java management extensions,简称JMX),是一个可以为Java应用程序或系统植入远程管理功能的框架, java中常用Console、VisualVM来查看和远程管理构件(MBean)
Tomcat容器状态
tomcat的容器状态有12中,具体定义可参见枚举 org.apache.catalina.LifecycleState 。
每个容器由于继承自LifecycleBase,当容器状态发生变化时,都会调用 fireLifecycleEvent 方法,生成LifecycleEvent,并且交由此容器的事件监听器处理。
/**
* 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);
}
每个容器在新建、初始化、启动,销毁,被添加到父容器的过程中都会调用父类LifecycleBase的addLifecycleListener方法,所有容器的起始状态为 LifecycleState.NEW

实际的处理过程都是由定义的 LifecycleSupport 来完成的。
容器生命周期
所有容器的转态转换由上到下进行的,即先执行父容器的状态转换及相关操作,然后再执行子容器的转态转换,这个过程是层层迭代执行的
比如,对于Server容器,在tomcat中具体实现是StandardServer,它的子容器是Service, StandardServer的初始化方法如下:
/**
* Invoke a pre-startup initialization. This is used to allow connectors to
* bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// Register global String cache
// 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
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
onameMBeanFactory = register(factory, "type=MBeanFactory");
// Register the naming resources
globalNamingResources.init();
// Populate the extension validator with JARs from common and shared
// class loaders
if (getCatalina() != null) {
ClassLoader cl = getCatalina().getParentClassLoader();
// Walk the class loader hierarchy. Stop at the system class loader.
// This will add the shared (if present) and common class loaders
while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
if (cl instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url : urls) {
if (url.getProtocol().equals("file")) {
try {
File f = new File(url.toURI());
if (f.isFile() && f.getName().endsWith(".jar")) {
ExtensionValidator.addSystemResource(f);
}
} catch (URISyntaxException e) {
// Ignore
} catch (IOException e) {
// Ignore
}
}
}
}
cl = cl.getParent();
}
}
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
容器初始化
每个容器的init方法是自身初始化的入口,其初始化过程如下图

图2
图中所说的具体容器,实际就是LifecycleBase的具体实现类,可参见 图1
根据tomcat源码分析,初始化过程步骤如下:
tomcat启动时,最先会调用方调用Server的init方法,即server容器父类LifecycleBase的init方法,LifecycleBase的init方法主要完成一些所有容器公共抽象出来的动作;
LifecycleBase使用了设计模式中的模版方法模式, init方法调用具体容器的initInternal方法实现,此initInternal方法用于对容器本身真正的初始化; initInternal方法实在LifecycleBase的子类中来实现的,
LifecycleBase中的的init方法
@Override
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
}
protected abstract void initInternal() throws LifecycleException;
比如LifecycleBase的子类StandardService中实现的initInternal方法,
/**
* Invoke a pre-startup initialization. This is used to allow connectors to bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException {
super.initInternal(); // 调用父类LifecycleMBeanBase的initInternal方法实现,此initInternal方法用于将容器托管到JMX
if (container != null) {
container.init();
}
// Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize mapper listener
mapperListener.init();
// Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
connector.init(); //调用子容器的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);
}
}
}
}
}
具体容器的initInternal方法调用父类LifecycleMBeanBase的initInternal方法实现,此initInternal方法用于将容器托管到JMX,便于运维管理;
/**
* Sub-classes wishing to perform additional initialization should override
* this method, ensuring that super.initInternal() is the first call in the overriding method.
*/
@Override
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());
}
}
LifecycleMBeanBase的initInternal方法调用自身的register方法,将容器作为MBean注册到MBeanServer;
protected final ObjectName register(Object obj, String objectNameKeyProperties) {
// Construct an object name with the right domain
StringBuilder name = new StringBuilder(getDomain()).append(":").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;
}
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(null, 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;
}
}
容器如果有子容器,会调用子容器的init方法;比如 StandardService 中的 子容器有Connector,会依次对所有的Connector调用init方法。
容器初始化完毕,LifecycleBase会将容器的状态更改为初始化完毕,即LifecycleState.INITIALIZED。
【总结】
Tomcat通过将内部所有组件都抽象为容器,为容器提供统一的生命周期管理,各个子容器只需要关心各自的具体实现
【tomcat8开启JMX】
修改tomcat中的文件 /bin/catalina.sh
找到下面的内容
# Uncomment the following line to make the umask available when using the
# org.apache.catalina.security.SecurityListener
#JAVA_OPTS="$JAVA_OPTS -Dorg.apache.catalina.security.SecurityListener.UMASK=`umask`"
CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote
-Djava.rmi.server.hostname=112.124.63.188
-Dcom.sun.management.jmxremote.port=8082
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false"
# ----- Execute The Requested Command -----------------------------------------
ip是你要监控的tomcat所在服务器的ip地址
port端口号,开启的监控端口号。
sll:是否开启ssl链接
authenticate, 是否需要授权,false表示不需要用户名和密码
也可以添加授权的的配置:
同样修改 /bin/catalina.sh
CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote
-Djava.rmi.server.hostname=112.124.63.188
-Dcom.sun.management.jmxremote.port=8082
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false"
-Dcom.sun.management.jmxremote.password.file=../conf/jmxremote.password
-Dcom.sun.management.jmxremote.access.file=../conf/jmxremote.access”
authenticate,true 开启鉴权功能
access.file,权限文件路径
password.file,密码文件路径
将 JAVA_HOME/jre/lib/management 下面的 jmxremote.access 和jmxremote.password.template 拷贝到 tomcat下的conf目录中
1:将jmxremote.password.template文件名修改为jmxremote.password,在此文件尾部添加用户名和密码
2:修改两个文件的权限
chmod 600 jmxremote.access
chmod 600 jmxremote.password
3:修改jmxremote.access文件,将文件最后两行显示【monitorRole和controlRole】的注释取消,
其中monitorRole为只拥有只读权限的角色,controlRole有更高权限:读写等。

浙公网安备 33010602011771号