Tomcat启动过程
一 Tomcat启动机制
二 Tomcat一键启停机制——LifeCycle
2.1 什么是LifeCycle
- LifeCycle接口定义如下,每个具体的组件去实现这些方法。
public interface Lifecycle {
// 添加监听器
public void addLifecycleListener(LifecycleListener listener);
// 获取所以监听器
public LifecycleListener[] findLifecycleListeners();
// 移除某个监听器
public void removeLifecycleListener(LifecycleListener listener);
// 初始化方法
public void init() throws LifecycleException;
// 启动方法
public void start() throws LifecycleException;
// 停止方法,和start对应
public void stop() throws LifecycleException;
// 销毁方法,和init对应
public void destroy() throws LifecycleException;
// 获取生命周期状态
public LifecycleState getState();
// 获取字符串类型的生命周期状态
public String getStateName();
}
- LifeCycle接口定义分为两部分,一部分是组件的生命周期方法,比如init()、start()、stop()、destroy()。另一部分是扩展接口就是状态和监听器。
- 因为所有的组件都实现了LifeCycle接口,如果可以在父组件的init()方法里创建子组件并调用子组件的init()方法,在父组件的start()方法里调用子组件的start()方法,那么调用者就可以无差别的调用各组件的init()方法和start()方法,并且只要调用最顶层组件,也就是Server组件的init()和start()方法,整个Tomcat就被启动起来了。
2.2 LifeCycleBase抽象类
- 定义一个基类来实现共同的逻辑,然后让各个子类去继承它,就达到了重用的目的。基类中往往会定义一些抽象方法,并调用这些方法来实现骨架逻辑,抽象方法是留给各个子类去实现,如下图所示:
- LifeCycleBase实现了LifeCycle接口中所有的方法,还定义了相应的抽象方法交给具体子类去实现,这是典型的模板设计模式。
- 简单看一下LifeCycleBase的init方法,如下:
2.3 LifeCycle扩展——事件监听机制
- 把组件的生命周期定义成一个个状态,把状态的转变看作是一个事件。而事件是有监听器的,在监听器里可以实现一些逻辑,并且监听器也可以方便的添加和删除,这就是典型的观察者模式。
- Tomcat定义一个枚举LifeCycleState来表示组件有哪些状态。
- 下面我们来看一下在上述init方法中触发事件监听的方法setStateInternal:
private synchronized void setStateInternal(LifecycleState state,
Object data, boolean check) throws LifecycleException {
if (log.isDebugEnabled()) { log.debug(sm.getString("lifecycleBase.setState", this, state));
}
if (check) {
// Must have been triggered by one of the abstract methods (assume
// code in this class is correct)
if (state == null) {
invalidTransition("null");
return;
}
// Any method can transition to failed
// startInternal() permits STARTING_PREP to STARTING
// stopInternal() permits STOPPING_PREP to STOPPING and FAILED to
// STOPPING
if (!(state == LifecycleState.FAILED ||
(this.state == LifecycleState.STARTING_PREP &&
state == LifecycleState.STARTING) ||
(this.state == LifecycleState.STOPPING_PREP &&
state == LifecycleState.STOPPING) ||
(this.state == LifecycleState.FAILED &&
state == LifecycleState.STOPPING))) {
// No other transition permitted
invalidTransition(state.name());
}
}
// 设置状态
this.state = state;
// 触发事件
String lifecycleEvent = state.getLifecycleEvent();
if (lifecycleEvent != null) {
fireLifecycleEvent(lifecycleEvent, data);
}
}
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
// lifecycleListeners为监听器数组
for (LifecycleListener listener : lifecycleListeners) {
// 触发监听器的监听逻辑
listener.lifecycleEvent(event);
}
}
- 监听器注册两种情况:
- Tomcat自定义了一些监听器:这些监听器是父组件在创建子组件的过程中注册到子组件的。比如MemoryLeakTrackingListener监听器,用来检测 Context容器中的内存泄漏,这个监听器是Host容器在创建Context容器时注册到Context中的
- 在server.xml中定义自己的监听器,Tomcat在启动时会解析 server.xml,创建监听器并注册到容器组件
三 Bootstrap启动入口
3.1 main()
public static void main(String args[]) {
synchronized (daemonLock) {
if (daemon == null) {
// Don't set daemon until init() has completed
Bootstrap bootstrap = new Bootstrap();
try {
// 1. 初始化Bootstrap,主要包括实例化Tomcat特有的类加载器和Catalina实例
bootstrap.init();
} catch (Throwable t) { handleThrowable(t); t.printStackTrace();
return;
}
daemon = bootstrap;
} else { Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
}
try {
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
} else if (command.equals("start")) { daemon.setAwait(true);
daemon.load(args);
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
} else if (command.equals("stop")) { daemon.stopServer(args);
} else if (command.equals("configtest")) {
daemon.load(args);
if (null == daemon.getServer()) {
System.exit(1);
}
System.exit(0);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
// Unwrap the Exception for clearer error reporting
if (t instanceof InvocationTargetException &&
t.getCause() != null) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(1);
}
}
- 核心方法就是调用init方法初始化deamon成员变量,以及当接收到start命令时,调用deamon的load和start方法启动。
3.2 init()
- Bootstrap的init方法:
public void init() throws Exception {
// 1. 初始化Tomcat类加载器
initClassLoaders(); Thread.currentThread().setContextClassLoader(catalinaLoader); SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
// 2. 实例化Catalina实例
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
// 3. 反射调用Catalina的setParentClassLoader方法,将sharedLoader设置为Catalina的parentClassLoader成员变量
if (log.isDebugEnabled())
log.debug("Setting startup class properties");
String methodName = "setParentClassLoader";
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
catalinaDaemon = startupInstance;
}
- 作用:创建commonLoader、catalinaLoader、sharedLoader类加载器;实例化org.apache.catalina.startup.Catalina对象,并赋值给静态成员 catalinaDaemon,以sharedLoader作为入参通过反射调用该对象的 setParentClassLoader方法
3.3 load()
private void load(String[] arguments) throws Exception {
// Call the load() method
String methodName = "load";
Object param[];
Class<?> paramTypes[];
if (arguments==null || arguments.length==0) {
paramTypes = null;
param = null;
} else {
paramTypes = new Class[1];
paramTypes[0] = arguments.getClass();
param = new Object[1];
param[0] = arguments;
}
Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes);
if (log.isDebugEnabled()) {
log.debug("Calling startup class " + method);
}
method.invoke(catalinaDaemon, param);
}
- 作用:通过反射调用catalinaDaemon对象的load方法,catalinaDaemon对象就是在上面的init方法中实例化的。Catalina的load方法,完成一些Tomcat组件的初始化操作。
3.4 start()
public void start() throws Exception {
if (catalinaDaemon == null) {
init();
}
Method method = catalinaDaemon.getClass().getMethod("start", (Class [])null);
method.invoke(catalinaDaemon, (Object [])null);
}
- 作用:通过反射调用catalinaDaemon对象上的start方法,该start方法,完成了Tomcat的启动。
四 Catalina
- 通过上面的介绍我们知道,Bootstrap的main方法中,就是调用了Catalina的load和start方法,从而完成了Tomcat的启动。
4.1 load()
public void load() {
if (loaded) {
return;
}
loaded = true;
long t1 = System.nanoTime();
initDirs();
// Before digester - it may be needed
initNaming();
// 1. 解析server.xml,实例化各Tomcat组件
parseServerXml(true);
Server s = getServer();
if (s == null) {
return;
}
// 2. 为Server组件实例设置Catalina相关成员value
getServer().setCatalina(this); getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
// Stream redirection
initStreams();
// 3. 调用Server组件的init方法,初始化Tomcat各组件
try {
getServer().init();
} catch (LifecycleException e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new java.lang.Error(e);
} else { log.error(sm.getString("catalina.initError"), e);
}
}
if(log.isInfoEnabled()) { log.info(sm.getString("catalina.init", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
}
}
- Catalina的init方法作用:完成server.xml的解析以及Tomcat各组建的初始化。
- parseServerXml()方法完成了xml配置到Tomcat各组件的Java对象的转化。
- getServer().init()方法,使用了我们上面介绍的LifeCycle机制,完成了Server及以下组件的初始化
4.2 start()
public void start() {
if (getServer() == null) {
load();
}
if (getServer() == null) { log.fatal(sm.getString("catalina.noServer"));
return;
}
long t1 = System.nanoTime();
// 1. start server
try {
getServer().start();
} catch (LifecycleException e) { log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}
if (log.isInfoEnabled()) { log.info(sm.getString("catalina.startup", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
}
// Register shutdown hook
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
} Runtime.getRuntime().addShutdownHook(shutdownHook);
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
false);
}
}
if (await) {
await();
stop();
}
}
主要是调用Server组件的start方法,启动Tomcat的所有组件。
到这里我们知道,Tomcat是通过调用Catalina的load()和start()方法,完成了Tomcat的启动。load()方法负责组件的初始化,start()方法负责组件的启动。关于Tomcat组件的初始化,其中Host及其下级组件的初始化比较特殊,并没有在Engine的初始化方法中完成Host及下级组件的初始化。另外Context的实例化和启动过程也比较特殊(在上述parseServerXml方法中,并没有实例化,是在Host组件启动时动态生成的),另外我们知道Tomcat web应用支持热更新,也与Context的启动策略有关。关于Context实例化和初始化过程,我们先挖个坑,本篇文章中不做详细介绍,在后面用专门的文章详细介绍。
五 Server
- Server组件的init和start方法:我们知道LifeCycle机制,init方法其实最终调用的是StandardServer的initInternal方法,start方法最终调用的是StandardServer的initInternal方法。
5.1 initInternal()
protected void initInternal() throws LifecycleException {
// 1. 调用LifeCycleMBeanBase的initInternal方法,注册JMX Mbean
super.initInternal();
// Initialize utility executor reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
register(utilityExecutor, "type=UtilityExecutor");
// 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");
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();
}
}
// 2. 调用所有子组件Service的init方法,初始化service
for (Service service : services) {
service.init();
}
}
5.2 startInternal()
protected void startInternal() throws LifecycleException {
// 1. 触发LifeCycle事件监听 fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);
globalNamingResources.start();
// 2. 启动所有Server的子组件Service
synchronized (servicesLock) {
for (Service service : services) {
service.start();
}
}
if (periodicEventDelay > 0) {
monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
new Runnable() {
@Override
public void run() { startPeriodicLifecycleEvent();
}
}, 0, 60, TimeUnit.SECONDS);
}
}
六 Service
6.1 initInternal()
protected void initInternal() throws LifecycleException {
super.initInternal();
if (engine != null) {
engine.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) {
connector.init();
}
}
}
- 往jmx中注册StandardService
初始化Engine - 存在Executor线程池,进行init操作,这个Excecutor是tomcat的接口,继承至java.util.concurrent.Executor和org.apache.catalina.Lifecycle
- 初始化Connector连接器,默认有http1.1、ajp连接器,而这个Connector初始化过程,又会对ProtocolHandler进行初始化
6.2 startInternal()
protected void startInternal() throws LifecycleException {
if(log.isInfoEnabled()) log.info(sm.getString("standardService.start.name", this.name));
setState(LifecycleState.STARTING);
// Start our defined Container first
if (engine != null) {
synchronized (engine) {
engine.start();
}
}
synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
mapperListener.start();
// Start our defined Connectors second
synchronized (connectorsLock) {
for (Connector connector: connectors) {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
}
}
}
- 跟上述initInternal方法比较类似,只不过是调用各子组件(Engine、Connector)的start方法,启动子组件。
七 Engine
7.1 initInternal()
protected void initInternal() throws LifecycleException {
// Ensure that a Realm is present before any attempt is made to start
// one. This will create the default NullRealm if necessary.
getRealm();
super.initInternal();
}
Engine的init()初始化方法中,并没有去调用子组件Host的init方法去初始化Host,在Host的start方法中进行Host组件的初始化,然后再进行启动操作。
- 出于效率考虑。Host、Context、Wrapper这些容器和具体的webapp应用相关联了,初始化过程会更加复杂耗时,在start阶段用多线程完成初始化以及start生命周期,效率可能会更高。
7.2 startInternal()
protected synchronized void startInternal() throws LifecycleException {
// Log our server identification information
if (log.isInfoEnabled()) { log.info(sm.getString("standardEngine.start", ServerInfo.getServerInfo()));
}
// Standard container startup
super.startInternal();
}
- 直接调用父类ContainerBase的startInternal方法。
protected synchronized void startInternal() throws LifecycleException {
// Start our subordinate components, if any
logger = null;
getLogger();
Cluster cluster = getClusterInternal();
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).start();
}
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
// Start our child containers, if any
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (Container child : children) { results.add(startStopExecutor.submit(new StartChild(child)));
}
MultiThrowable multiThrowable = null;
for (Future<Void> result : results) {
try {
result.get();
} catch (Throwable e) { log.error(sm.getString("containerBase.threadedStartFailed"), e);
if (multiThrowable == null) {
multiThrowable = new MultiThrowable();
}
multiThrowable.add(e);
}
}
if (multiThrowable != null) {
throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"), multiThrowable.getThrowable());
}
// Start the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
setState(LifecycleState.STARTING);
// Start our thread
if (backgroundProcessorDelay > 0) {
monitorFuture = Container.getService(ContainerBase.this).getServer() .getUtilityExecutor().scheduleWithFixedDelay(
new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
}
}
- ContainerBase中,使用了多线程来启动子组件。并且在最后通过setState(LifecycleState.STARTING)触发了事件监听。
- 启动子组件,使用了线程池,将子组件的启动任务提交到线程池。
private static class StartChild implements Callable<Void> {
private Container child;
public StartChild(Container child) {
this.child = child;
}
@Override
public Void call() throws LifecycleException {
child.start();
return null;
}
}
- 线程池中执行的任务最终调用的还是子组件(Host)的start()方法。我们来看一下Host的start()方法,其实Host的start方法是继承自ContainerBase的,如下:
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);
}
try { setStateInternal(LifecycleState.STARTING_PREP, null, false);
startInternal();
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);
}
} catch (Throwable t) {
// This is an 'uncontrolled' failure so put the component into the
// FAILED state and throw an exception.
handleSubClassException(t, "lifecycleBase.startFail", toString());
}
}
- 如果如果state为LifecycleState.NEW,会先进性初始化,然后在调用startInternal启动组件。这就是我们上面介绍的,Engine并没有在init方法中初始化子组件Host,而是在启动过程start方法中初始化的。
八 Host
8.1 initInternal()
protected void initInternal() throws LifecycleException { reconfigureStartStopExecutor(getStartStopThreads());
super.initInternal();
}
- Host的init方法中也没有初始化子组件Context,Context组件的初始化跟随者Context的启动start过程初始化的。
8.2 startInternal()
protected synchronized void startInternal() throws LifecycleException {
// Set error report valve
String errorValve = getErrorReportValveClass();
if ((errorValve != null) && (!errorValve.equals(""))) {
try {
boolean found = false;
Valve[] valves = getPipeline().getValves();
for (Valve valve : valves) {
if (errorValve.equals(valve.getClass().getName())) {
found = true;
break;
}
}
if(!found) {
Valve valve =
(Valve) Class.forName(errorValve).getConstructor().newInstance();
getPipeline().addValve(valve);
}
} catch (Throwable t) { ExceptionUtils.handleThrowable(t);
log.error(sm.getString( "standardHost.invalidErrorReportValveClass",
errorValve), t);
}
}
super.startInternal();
}
可以看到,Host的start方法中,仅仅是为Host的piline添加了一些Valve,随后调用了父类ContainerBase的startInternal方法。虽然ContainerBase的startInternal方法中可以启动子组件,但好像server.xml中并么有配置Host子组件Context相关的信息,所以也没办法去启动Context组件。