并注册烧写钩子 获取启动介质类型_Tomcat源码分析【八】启动过程分析之注册关闭钩子、阻塞监听关闭指令...

本篇主要分析Catalina的start方法内部的两块内容:注册关闭钩子与阻塞监听关闭指令。

注册关闭钩子

// Register shutdown hook

//注册关闭钩子

if (useShutdownHook) {

if (shutdownHook == null) {

// Catalina.this.stop();

shutdownHook = new CatalinaShutdownHook();

}

Runtime.getRuntime().addShutdownHook(shutdownHook);

// If JULI is being used, disable JULI's shutdown hook since

// shutdown hooks run in parallel and log messages may be lost

// if JULI's hook completes before the CatalinaShutdownHook()

LogManager logManager = LogManager.getLogManager();

if (logManager instanceof ClassLoaderLogManager) {

((ClassLoaderLogManager) logManager).setUseShutdownHook(

false);

}

}

注册新的虚拟机来关闭钩子。

只是一个已初始化但尚未启动的线程。虚拟机开始启用其关闭序列时,它会以某种未指定的顺序启动所有已注册的关闭钩子,

并让它们同时运行。运行完所有的钩子后,如果已启用退出终结,那么虚拟机接着会运行所有未调用的终结方法。

最后,虚拟机会暂停。注意,关闭序列期间会继续运行守护线程,如果通过调用方法来发起关闭序列,

那么也会继续运行非守护线程。

注册关闭钩子,CatalinaShutdownHook,当关闭时,会执行CatalinaShutdownHook中的方法:

@Override

public void run() {

try {

if (getServer() != null) {

Catalina.this.stop();

}

} catch (Throwable ex) {

ExceptionUtils.handleThrowable(ex);

log.error(sm.getString("catalina.shutdownHookFail"), ex);

} finally {

// If JULI is used, shut JULI down *after* the server shuts down

// so log messages aren't lost

LogManager logManager = LogManager.getLogManager();

if (logManager instanceof ClassLoaderLogManager) {

((ClassLoaderLogManager) logManager).shutdown();

}

}

}

}

调用 Catalina.this.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);

}

}

移除钩子,调用Server组件的stop、destroy生命周期方法,这个我打算后期专门分析。

阻塞监听关闭指令

// Bootstrap中会设置await为true,其目的在于让tomcat在shutdown端口阻塞监听关闭命令

if (await) {

//等待收到正确的关机命令,然后返回。

await();

//停止现有的服务器实例。

stop();

}

主要看StandardServer的await方法:

/**

* Wait until a proper shutdown command is received, then return.

* 等待收到正确的关机命令,然后返回。

* This keeps the main thread alive - the thread pool listening for http

* connections is daemon threads.

* 这使主线程保持活动状态——侦听http连接的线程池是守护进程线程。

*/

@Override

public void await() {

// Negative values - don't wait on port - tomcat is embedded or we just don't like ports

//负值——不要等待端口——tomcat是嵌入式的,或者我们只是不喜欢端口

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;

}

try {

awaitThread = Thread.currentThread();

// Loop waiting for a connection and a valid command

//循环等待连接和有效命令

while (!stopAwait) {

ServerSocket serverSocket = awaitSocket;

if (serverSocket == null) {

break;

}

// Wait for the next connection

Socket socket = null;

StringBuilder command = new StringBuilder();

try {

InputStream stream;

long acceptStartTime = System.currentTimeMillis();

try {

socket = serverSocket.accept();

socket.setSoTimeout(10 * 1000); // Ten seconds

stream = socket.getInputStream();

} catch (SocketTimeoutException ste) {

// This should never happen but bug 56684 suggests that

// it does.

log.warn(sm.getString("standardServer.accept.timeout",

Long.valueOf(System.currentTimeMillis() - acceptStartTime)), ste);

continue;

} 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;

}

// Control character or EOF (-1) terminates loop

if (ch < 32 || ch == 127) {

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

//"SHUTDOWN"

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"指令,否则一直循环。

至此,简单的分析完了Tomcat的启动过程,这里简单概述一下:

server.xml配置文件解析,组装Catalina实例,注入Server\Service\Connector\Engine\Host\Context等组件。

执行各个组件的生命周期函数init,做初始化操作,通知监听器等。

执行各组件的start方法,最重要是Connector的start

Connector开启服务Socket,创建并启动acceptor, poller线程等。

注册关闭钩子与阻塞监听关闭指令。

之后的文章分析如何处理客户端的请求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值