Java 服务优雅关闭
进程收到终止信号后如何排空请求、关闭线程池与释放连接。
#concept
#growing
#dev
#java
[!info] related notes
Java 服务优雅关闭
这个概念为什么出现
容器编排滚动发布会发 SIGTERM。若进程直接退出,在途请求会中断、未提交任务可能丢失;OS 会回收进程资源,但不替应用完成事务、确认消息或刷新缓冲。优雅关闭定义停止接流 → 排空 → 释放的顺序。
[!abstract] 一句话理解 捕获终止信号/钩子,停止接收新请求,等待在途完成(有超时),再关闭线程池与外部连接。
最小可运行示例
场景:注册关闭钩子停止线程池
import java.util.concurrent.*;
public class GracefulDemo {
public static void main(String[] args) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(2);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("shutdown-begin");
pool.shutdown();
try {
if (!pool.awaitTermination(2, TimeUnit.SECONDS)) {
pool.shutdownNow();
if (!pool.awaitTermination(1, TimeUnit.SECONDS)) {
System.err.println("workers-not-terminated");
}
}
} catch (InterruptedException e) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}
System.out.println("shutdown-end terminated=" + pool.isTerminated());
}));
pool.execute(() -> System.out.println("work"));
System.exit(0); // 触发钩子(演示)
}
}
javac GracefulDemo.java && java GracefulDemo
正常运行输出包含 work、shutdown-begin 和 shutdown-end terminated=true。work 与 shutdown-begin 的先后取决于调度;关闭会等待已提交任务,本例无需用 sleep 猜测其完成时间。
结合场景再看三个关注点
- 钩子必须有时间预算;shutdownNow 只是尝试中断,不会强杀 Java 线程。进程最终强制终止由运行环境负责,不能把钩子返回当作任务已成功完成。
- HTTP 服务器 stop 延迟参数。
- 与 K8s preStop/terminationGracePeriod 对齐。
核心概念与准确模型
1. 信号与钩子
正常 JVM 关闭(包括 System.exit,以及通常配置下的终止信号)会启动已注册钩子。多个钩子并发执行、顺序未指定;不要依赖其他钩子先完成。Runtime.halt、SIGKILL、崩溃等可能完全跳过钩子。
2. 排空策略
先停止接入请求、消费消息和调度新任务,再等待在途任务并按依赖顺序释放连接。readiness 变化传播需要时间,也不会立即切断已有连接;还需服务本身停止接收新工作。Kubernetes 的 preStop 时间计入终止宽限期,应把整个流程放进同一个时间预算。
3. 数据一致性:在途事务
停机无法保证所有在途工作完成;应结合事务、可重试/幂等操作与消息确认时机。不要在结果尚未确定时仅因进入关闭流程就确认任务成功。
边界情况与反直觉行为
- 钩子中死锁。
- 日志框架先关闭导致钩子日志丢失。
- kill -9 无法优雅。
常见误区
[!warning] 常见误区:只关 HTTP 不关消费者线程 消息消费/定时任务同样要停。
工程实践
- readiness 先 fail,再停服务。
- 超时可配置。
- 指标记录关闭耗时。
- 演练强制杀死与优雅路径。
本节总结
- 停新→排空→释放
- 超时兜底
- 与编排对齐
自测题
- 为什么 readiness 要先变假?
- shutdown 与 shutdownNow?
参考答案
- 通知就绪性检查的使用方停止路由新流量;这有传播延迟,不能代替服务端停止接入。
- shutdown 拒绝新任务但执行已提交任务;shutdownNow 尝试中断运行任务并返回尚未开始的任务列表,二者都不等待终止,需另用 awaitTermination。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| Runtime.addShutdownHook | API | 钩子 |