观察org.apache.catalina.startup.Catalina.java中的start()方法,我们发现Serever启动后,还要执行如下代码:
/**
* Await and shutdown.
*/
public void await() {
getServer().await();
}
查询await()方法的实现,将跟到org.apache.catalina.core.StandServer中await()方法。
try {
awaitThread = Thread.currentThread();
// Loop waiting for a connection and a valid command
while (!stopAwait) {
// ServerSocket[addr=localhost/127.0.0.1,localport=8005]
ServerSocket serverSocket = awaitSocket;
if (serverSocket == null) {
break;
}
// Wait for the next connection
Socket socket = null;
StringBuilder command = new StringBuilder();
try {
InputStream stream;
try {
socket = serverSocket.accept();
socket.setSoTimeout(10 * 1000); // Ten seconds
stream = socket.getInputStream();
} catch (AccessControlException ace) {
log.warn("StandardServer.accept security exception: "
+ ace.getMessage(), ace);
continue;
} catch (IOException e) {
if (stopAwait) {
// Wait was aborted with socket.close()
break;
}
log.error("StandardServer.await: accept: ", e);
break;
}
// Read a set of characters from the socket
int expected = 1024; // Cut off to avoid DoS attack
while (expected < shutdown.length()) {
if (random == null)
random = new Random();
expected += (random.nextInt() % 1024);
}
while (expected > 0) {
int ch = -1;
try {
ch = stream.read();
} catch (IOException e) {
log.warn("StandardServer.await: read: ", e);
ch = -1;
}
if (ch < 32) // Control character or EOF terminates loop
break;
command.append((char) ch);
expected--;
}
} finally {
// Close the socket now that we are done with it
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
// Ignore
}
}
// Match against our command string
boolean match = command.toString().equals(shutdown);
if (match) {
log.info(sm.getString("standardServer.shutdownViaPort"));
break;
} else
log.warn("StandardServer.await: Invalid command '"
+ command.toString() + "' received");
}
} finally {
ServerSocket serverSocket = awaitSocket;
awaitThread = null;
awaitSocket = null;
// Close the server socket and return
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
// Ignore
}
}
}
根据该方法上的注释可以明白:该方法将一直等待,直到接收到一个合适的shutdown命令才会返回。这样会使主线程一直存活——监听http连接的线程池是守护线程。
该方法在默认地址的默认端口上(ServerSocket[addr=localhost/127.0.0.1,localport=8005])监听Socket连接,当发现监听的连接输入流中的内容与默认配置的值匹配(该值默认为字符串SHUTDOWN)则跳出循环,该方法返回。否则,该方法会一直循环执行下去。
默认端口的配置见Server.xml:<Server port="8005" shutdown="SHUTDOWN">
如下,当我们敲击”localhost:8005/SHUTDOWN”的url时,就会跳出循环。
再次观察如下代码:
// Match against our command string
boolean match = command.toString().equals(shutdown);
if (match) {
log.info(sm.getString("standardServer.shutdownViaPort"));
break;
} else
log.warn("StandardServer.await: Invalid command '"
+ command.toString() + "' received");
从输入流中读出的int转换为字符串后是”GET /SHUTDOWN HTTP/1.1”,而shutdown是默认值”SHUTDOWN”,最终无法跳出while循环,控制台也报出了超时的错误。
十二月 01, 2016 11:31:06 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: Invalid command 'GET /SHUTDOWN HTTP/1.1' received
十二月 01, 2016 11:31:12 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: Invalid command '' received
十二月 01, 2016 11:31:24 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: Invalid command 'GET /SHUTDOWN HTTP/1.1' received
十二月 01, 2016 11:31:29 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: Invalid command 'GET /SHUTDOWN HTTP/1.1' received
十二月 01, 2016 11:31:30 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: Invalid command 'GET /SHUTDOWN HTTP/1.1' received
十二月 01, 2016 11:31:40 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: read:
java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at org.apache.catalina.core.StandardServer.await(StandardServer.java:478)
at org.apache.catalina.startup.Catalina.await(Catalina.java:727)
at org.apache.catalina.startup.Catalina.start(Catalina.java:673)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:322)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:450)
十二月 01, 2016 11:31:40 下午 org.apache.catalina.core.StandardServer await
警告: StandardServer.await: Invalid command '' received
那如何通过在线的方式关闭服务器呢?
最简单的方法就是更改8005端口号的默认值为”GET /SHUTDOWN HTTP/1.1”,并重启服务,再次输入”localhost:8005/SHUTDOWN”,即可关闭Tomcat服务器。
最后我们再来看org.apache.catalina.startup.Catalina类中stop方法,
/**
* Stop an existing server instance.
*/
public void stop() {
try {
// Remove the ShutdownHook first so that server.stop()
// doesn't get invoked twice
if (useShutdownHook) {
Runtime.getRuntime().removeShutdownHook(shutdownHook);
// If JULI is being used, re-enable JULI's shutdown to ensure
// log messages are not lost
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
true);
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}
// Shut down the server
try {
Server s = getServer();
LifecycleState state = s.getState();
if (LifecycleState.STOPPING_PREP.compareTo(state) <= 0
&& LifecycleState.DESTROYED.compareTo(state) >= 0) {
// Nothing to do. stop() was already called
} else {
s.stop();
s.destroy();
}
} catch (LifecycleException e) {
log.error("Catalina.stop", e);
}
}
移除完关闭钩子后,守护线程
1.调用stop()方法,和启动过程类似,会先后调用StandardServer和StandardService类的stopInternal()方法、加锁暂停HTTP Connector和AJP Connector的连接、加锁停止StandardEngine、再加锁停止HTTP Connector和AJP Connector的连接、调用NamingResources的stopInternal()方法等。
2.调用destroy()方法,会先后调用StandardServer和StandardService类的destroyInternal()方法、加锁销毁Connector、加锁销毁StandardEngine、销毁NamingResources等。
看如下代码:
/**
* Sub-classes wishing to perform additional clean-up should override this
* method, ensuring that super.destroyInternal() is the last call in the
* overriding method.
*/
@Override
protected void destroyInternal() throws LifecycleException {
// oname -> Catalina:type=Service
unregister(oname);
}
各组件的destroy()方法执行完后要调用父类org.apache.catalina.util.LifecycleMBeanBase中的destroyInternal(),这意味着生命周期的结束!
若通过执行shutdown.bat或shutdown.sh脚本,最终会执行org.apache.catalina.startup.Bootstrap类的main方法,并传入参数”stop”,接着将调用org.apache.catalina.startup.Catalina类中的stopServer()方法,步骤和上述部分相似,这里就不再赘述。