前言
前面我们已经经历了tomcat的启动和请求接收处理,本节我们来看看tomcat的停止过程。
Bootstrap
tomcat的停止仍然是从Bootstrap开始的,当我们输入的命令为stop
的时候,tomcat会开始进入停止的流程,源码如下:
else if (command.equals("stop")) {
daemon.stopServer(args);
}
在tomcat启动的章节中,我们已经知道daemon
其实就是Bootstrap的实例化对象,这里调用的是stopServer
方法,源码如下:
public void stopServer(String[] arguments)
throws Exception {
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("stopServer", paramTypes);
method.invoke(catalinaDaemon, param);
}
这里我们可以看到,依然是代理了Catalina类的stopServer
方法。
Catalina
那么我们来看一下真正的结束方法:
public void stopServer(String[] arguments) {
if (arguments != null) {
arguments(arguments);
}
Server s = getServer();
if (s == null) {
// ... 省略不影响理解的代码
} else {
try {
//【1】如果server不为null,则停止服务
s.stop();
} catch (LifecycleException e) {
log.error("Catalina.stop: ", e);
}
return;
}
//【2】结束服务并关闭应用
s = getServer();
if (s.getPort()>0) {
try (Socket socket = new Socket(s.getAddress(), s.getPort());
OutputStream stream = socket.getOutputStream()) {
String shutdown = s.getShutdown();
for (int i = 0; i < shutdown.length(); i++) {
stream.write(shutdown.charAt(i));
}
stream.flush();
} catch (ConnectException ce) {
log.error(sm.getString("catalina.stopServer.connectException",
s.getAddress(),
String.valueOf(s.getPort())));
log.error("Catalina.stop: ", ce);
System.exit(1);
} catch (IOException e) {
log.error("Catalina.stop: ", e);
System.exit(1);
}
} else {
log.error(sm.getString("catalina.stopServer"));
System.exit(1);
}
}
这里我们可以看到,有两个分支结束应用,分支【2】很简单,主要是通过socket调用tomcat的关闭钩子来关闭tomcat,而分支【1】则是直接通过stop
方法来结束应用,我们来看一下这个方法:
public void stop() {
try {
// 【1】移除停止tomcat的钩子
if (useShutdownHook) {
Runtime.getRuntime().removeShutdownHook(shutdownHook);
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
true);
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
// 【2】停止应用
try {
Server s = getServer();
LifecycleState state = s.getState();
if (LifecycleState.STOPPING_PREP.compareTo(state) <= 0
&& LifecycleState.DESTROYED.compareTo(state) >= 0) {
} else {
s.stop();
s.destroy();
}
} catch (LifecycleException e) {
log.error("Catalina.stop", e);
}
}
这里可以看到,首先移除了启动的时候添加的停止tomcat的钩子,这里主要是为了防止多次停止,之后还是判断tomcat目前的状态,是否正在停止的过程,如果不是的话,会直接调用server的stop
方法和destroy
方法,先停止服务,最后销毁服务,完成一个完整的生命周期。
Server
Tomcat的停止过程和启动过程相似,从顶层容器Server开始逐层往下调用stop方法,我们来看一下server的stop
:
@Override
protected void stopInternal() throws LifecycleException {
setState(LifecycleState.STOPPING);
fireLifecycleEvent(CONFIGURE_STOP_EVENT, null);
// 【1】停止service
for (int i = 0; i < services.length; i++) {
services[i].stop();
}
// 【2】停止全局命名资源
globalNamingResources.stop();
// 【3】等待停止完成
stopAwait();
}
这里还是先停止所有的service对象,然后停止全局资源,之后就阻塞线程等待全部组件停止完成。
这里我们主要看service的停止过程:
protected void stopInternal() throws LifecycleException {
// 【1】暂停所有连接器
synchronized (connectorsLock) {
for (Connector connector: connectors) {
try {
connector.pause();
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.pauseFailed",
connector), e);
}
}
}
if(log.isInfoEnabled())
log.info(sm.getString("standardService.stop.name", this.name));
setState(LifecycleState.STOPPING);
// 【2】停止Container
if (container != null) {
synchronized (container) {
container.stop();
}
}
// 【3】停止连接器
synchronized (connectorsLock) {
for (Connector connector: connectors) {
if (!LifecycleState.STARTED.equals(
connector.getState())) {
// Connectors only need stopping if they are currently
// started. They may have failed to start or may have been
// stopped (e.g. via a JMX call)
continue;
}
try {
connector.stop();
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.stopFailed",
connector), e);
}
}
}
// 【4】停止全部映射监听器
if (mapperListener.getState() != LifecycleState.INITIALIZED) {
mapperListener.stop();
}
// 【5】停止线程池
synchronized (executors) {
for (Executor executor: executors) {
executor.stop();
}
}
}
service的停止过程比较麻烦,需要停止4个组件:
- 连接器
- 容器(Engine)
- 监听器
- 线程池
在停止过程,我们注意到,对于连接器,是先暂停了,然后等待容器停止完成之后才停止,这里先暂停连接器是停止再接收请求,等待容器停止是为了保证已经接收的请求全部处理完成,最后才能停止连接器。
这里我们不再对每个组件的停止详细介绍了,在tomcat启动的过程中,我们知道,连接器代理了协议处理器(ProtocolHandler),而协议处理器代理了协议入口org.apache.tomcat.util.net.AbstractEndpoint
,这里我们还是以HTTP协议为例,来看一下org.apache.tomcat.util.net.NioEndpoint
中的stopInternal
的实现:
@Override
public void stopInternal() {
//【1】释放弹簧连接
releaseConnectionLatch();
if (!paused) {
pause();
}
if (running) {
running = false;
// 【2】释放接收器线程
unlockAccept();
// 【3】清除所有辨识线程
for (int i=0; pollers!=null && i<pollers.length; i++) {
if (pollers[i]==null) continue;
pollers[i].destroy();
pollers[i] = null;
}
try {
stopLatch.await(selectorTimeout + 100, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignore) {
}
//【4】停止线程池
shutdownExecutor();
//【5】清除所有相关对象
eventCache.clear();
nioChannels.clear();
processorCache.clear();
}
}
停止的过程比较简单,主要是停止由自己启动的线程和对象即可。
所有的停止完成后,基本上会按照相同的流程在执行一次destory
。
这个过程就不同全部写出来了,并没有什么复杂的逻辑,请同学们自己花点时间了解一下即可。
总结
tomcat停止的过程和启动的过程类似,都是从上往下逐层调用生命周期中的方法,并且清理每个组件自己生成的对象和内部持有的组件即可。