1. 启动
以下是 tomcat startup.sh 启动脚本最终调用的启动方法,org.apache.catalina.startup.Catalina#start:
public void start() {
if (getServer() == null) {
load();
}
if (getServer() == null) {
log.fatal("Cannot start server. Server instance is not configured.");
return;
}
// Start the new 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 (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
if (await) {
await();
stop();
}
}
可以看到,上面方法最后是调用了 org.apache.catalina.core.StandardServer#await() 方法:
@Override
public void await() {
// Negative values - don't wait on port - tomcat is embedded or we just don't like ports
if (port == -2) {
// undocumented yet - for embedding apps that are around, alive.
return;
}
if (port==-1) {
try {
awaitThread = Thread.currentThread();
while(!stopAwait) {
try {
Thread.sleep( 10000 );
} catch( InterruptedException ex ) {
// continue and check the flag
}
}
} finally {
awaitThread = null;
}
return;
}
// Set up a server socket to wait on
try {
awaitSocket = new ServerSocket(port, 1,
InetAddress.getByName(address));
} catch (IOException e) {
log.error("StandardServer.await: create[" + address
+ ":" + port
+ "]: ", e);
return;
}
...
}
这里 StandardServer#await() 方法根据 server.xml 中的 Server 节点 port 配置分了几种情况进行处理:
- port 为 -2 时,函数直接退出,此时主线程不会阻塞;
- port 为 -1 时,将等待线程设置为当前线程,并且进入 while 循环,直到 stopAwait 标志位置为 true;
- port 为其他时,会新建一个 socket 服务端,该 socket 绑定了当前服务器的 IP 以及 Port 端口,随后将等待线程设置为当前线程,并且 socket 进入阻塞监听状态,知道 socket 监听到了 server.xml 中预置的关闭字符串(默认是 SHUTDOWN)
2. 关闭
一般来说,我们常用的有两种关闭 tomcat 的方式,一种是用官方提供的关闭脚本 shutdown.sh 进行关闭,另一种时通过 kill -x [pid] 进行关闭。
2.1. 通过 shutdown.sh 脚本关闭 tomcat
查看 shutdown.sh 脚本的内容,发现最终是调用了 BootStrap#main 方法进行关闭,传的是 "stop" 作为参数。改传参会导致调用 Catalina#stopServer 方法。
具体步骤是:初始化组件 -> 获取 Server 组件 -> 新建 socket 客户端 -> 发送消息至 socket 服务端
显然,这里对应的是 StandardServer#await() 方法的情况3,发送的字符串使得主线程结束阻塞状态,调用各组件的 stop() 和 destroy 方法对资源进行释放。
2.2 kill -x 的方式进行关闭
kill -x 的方式常用的有 kill -9 和 kill -15,相对来说,kill -9 会立即强制结束当前进程,这个操作既方便,但同时也极具破坏性;另外 kill -15 会向进程发送一个 TERM 的中断信号量,再JVM接收到该信号量后,会响应中断,从而结束该进程。JVM在结束当前进程前,会执行一系列名为 shutdownhook 的线程,而 Tomcat 在启动时,会向JVM注册了一个名为 CatalinaShutdownHook 的线程,该线程方法内调用了 Catalina#stop() 方法。