背景
在高并发场景下,服务发布或异常退出时,未完成的数据处理(如订单支付、资源释放)可能导致数据不一致。例如:服务在无事务保护下中断,会使部分数据滞留于中间状态(如 “处理中”“锁定”),需人工介入修复。为提升系统稳定性,引入优雅关闭机制,确保服务终止前完成必要的资源清理和数据持久化
用法
在Spring中提供了一个钩子,当jvm收到关闭通知之后(Kill -15),就会调用钩子中的方法,钩子中的方法执行完成后才会退出服务。
服务启动类中添加,Runtime.getRuntime().addShutdownHook(Thread) 需要传入一个线程对象,后续动作将会在该异步线程内完成
JVM 层面的 ShutdownHook,可直接注册 JVM 关闭钩子:
public class Application {
public static void main(String[] args) {
// 注册关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
log.info("Shutdown hook triggered, performing cleanup...");
try {
// 资源释放逻辑(如关闭连接池)
databaseClient.close();
cacheClient.disconnect();
log.info("Cleanup completed, exiting");
} catch (Exception e) {
log.error("Error during shutdown", e);
}
}, "shutdown-hook-thread"));
// 应用启动逻辑
startServer();
}
}
Spring 框架集成方案
Spring 提供DisposableBean、@PreDestroy和ApplicationListener<ContextClosedEvent>等多种优雅关闭方案
@Component
public class ThreadPoolCollect implements DisposableBean {
/**
* 优雅关闭线程池
*/
@Override
public void destroy() throws Exception {
shutdownThreadPool(commonThreadPool, "commonThreadPool");
shutdownThreadPool(shardMetaThreadPool, "shardMetaThreadPool");
}
}
除了主动关闭应用(使用 kill -15 指令),以下场景也将会触发 ShutdownHook :
代码执行结束,JVM 正常退出
应用代码中调用 System#exit 方法
应用中发生 OOM 错误,导致 JVM 关闭
终端中使用 Ctrl+C(非后台运行)
原理分析
后面有空再补充。
注意事项
1、ShutdownHook 需要尽快执行结束,比如使用while(true) 会导致服务一直没办法倍关闭;
2、不能使用Kill -9关闭服务,会导致不会调用shutdownHook;
3、shutdownHook的方法应该是线程安全的,因为可能发送信号导致方法被不同的线程多次调用;
4、shutdownHook调用过程中产生的所有异常都会被忽略掉;