安全发布之Jvm友好关闭
2017-11-30 本文已影响142人
3c69b7c624d9
小伙伴们一定很熟悉kill -9 来杀某个进程了,其实这样做在生产系统中是存在安全隐患的。
我们知道线程分为守护线程和用户线程。如果是系统中存在的均是守护线程,那么调用JVM的关闭讲课以关闭否则(存在至少一个用户线程)是无法正常关闭的。
因此代码能否响应中断是一个很重要的标志。
我们熟悉的Java的GC线程
133016_Bq7z_871390.png就是一个很典型的守护线程。
各位在关闭tomcat时,在使用到线程池时经常会碰到调用tomcat的关闭脚本无法正常关闭就是因为系统中存在一些非守护线程导致无法正常退出导致。
那么各位可能想了,我将所有的线程池起的线程都设置为守护线程那么在调用shutdown的时候是否时就会正常关闭呢?
thread.setDaemon(true);
答案是肯定的,但是带来一个其他的问题===》如果此时代码只执行了一半那么怎么处理呢?比如你消费了一个jms的消息,但是没有消费完成,然后‘砰’所有的线程灰飞烟灭,恰巧这个消息里面是个1000w的大单……
严重: The web application [] registered the JDBC driver [com.mysql.jdbc.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
2013-1-8 15:02:53 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
严重: The web application [] appears to have started a thread named [Thread-2] but has failed to stop it. This is very likely to create a memory leak.
2013-1-8 15:02:53 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
严重: The web application [] appears to have started a thread named [thread-snatch-picture] but has failed to stop it. This is very likely to create a memory leak.
2013-1-8 15:02:53 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
严重: The web application [] appears to have started a thread named [Xmemcached-Reactor-0] but has failed to stop it. This is very likely to create a memory leak.
2013-1-8 15:02:53 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
严重: The web application [] appears to have started a thread named [Xmemcached-Reactor-1] but has failed to stop it. This is very likely to create a memory leak.
2013-1-8 15:02:53 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
严重: The web application [] appears to have started a thread named [Xmemcached-Reactor-2] but has failed to stop it. This is very likely to create a memory leak.
2013-1-8 15:02:53 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
严重: The web application [] appears to have started a thread named [Xmemcached-Reactor-3] but has failed to stop it. This is very likely to create a memory leak.
通常我们调用tomcat关闭脚本如果没有关闭那么log重可能吐出类似下面的日志
某些小伙伴还可能看到如下的log
Exception in thread "Xmemcached-Reactor-3" java.lang.NoClassDefFoundError: org/apache/log4j/spi/ThrowableInformation
at org.apache.log4j.spi.LoggingEvent.(LoggingEvent.java:159)
at org.apache.log4j.Category.forcedLog(Category.java:391)
at org.apache.log4j.Category.log(Category.java:856)
at org.slf4j.impl.Log4jLoggerAdapter.info(Log4jLoggerAdapter.java:382)
at com.captaindebug.longpoll.service.DeferredResultService.run(DeferredResultService.java:75)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.ClassNotFoundException: org.apache.log4j.spi.ThrowableInformation
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
... 6 more
上述的原因是由于出现类卸载之后使用了一些已经不存在类加载器的类导致报错
小伙伴可能觉得太Easy了,怎么能难道有一定经验的开发小伙伴呢?
kill -9 `ps -ef|grep <tomcat>|awk '{print $2}'`
当然下次启动时没有问题了,但是可能就导致某些消费没有完成。
本篇重点来了 ShutDownHook 顾名思义这是在Jvm关闭时的钩子函数
“一个shutdown hook就是一个初始化但没有启动的线程。 当虚拟机开始执行关闭程序时,它会启动所有已注册的shutdown hook(不按先后顺序)并且并发执行。”
比如我们查看一下tomcat的shutdownhook
/**
* Start a new server instance.
*/
public void start() {
if (getServer() == null) {
load();
}
if (getServer() == null) {
log.fatal("Cannot start server. Server instance is not configured.");
return;
}
long t1 = System.nanoTime();
// 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;
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
}
// Register shutdown hook
if (useShutdownHook) {
if (shutdownHook == null) {
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);
}
}
if (await) {
await();
stop();
}
}
再比如Dubbo中各个组件中
static {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
if (logger.isInfoEnabled()) {
logger.info("Run shutdown hook now.");
}
ProtocolConfig.destroyAll();
}
}, "DubboShutdownHook"));
}
我们可以在对应使用清理Hook