Java 服务优雅关闭
进程收到终止信号后如何排空请求、关闭线程池与释放连接。
#type / concept
#status / growing
#tech / dev
#resource / java
[!info] 关联笔记
Java 服务优雅关闭
这个概念为什么出现
容器编排滚动发布会发 SIGTERM。若进程直接死,请求半截、连接泄漏、任务丢失。优雅关闭定义停止接流 → 排空 → 释放的顺序。
[!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();
}
} catch (InterruptedException e) {
pool.shutdownNow();
}
System.out.println("shutdown-end");
}));
pool.execute(() -> System.out.println("work"));
Thread.sleep(200);
System.exit(0); // 触发钩子(演示)
}
}
javac GracefulDemo.java && java GracefulDemo
期望输出包含 work 与 shutdown-begin/end(顺序可围绕 exit 语义)。
结合场景再看三个关注点
- 钩子要快,超时强杀。
- HTTP 服务器 stop 延迟参数。
- 与 K8s preStop/terminationGracePeriod 对齐。
核心概念与准确模型
1. 信号与钩子
2. 排空策略
3. 数据一致性:在途事务
边界情况与反直觉行为
- 钩子中死锁。
- 日志框架先关闭导致钩子日志丢失。
- kill -9 无法优雅。
常见误区
[!warning] 常见误区:只关 HTTP 不关消费者线程 消息消费/定时任务同样要停。
工程实践
- readiness 先 fail,再停服务。
- 超时可配置。
- 指标记录关闭耗时。
- 演练强制杀死与优雅路径。
本节总结
- 停新→排空→释放
- 超时兜底
- 与编排对齐
自测题
- 为什么 readiness 要先变假?
- shutdown 与 shutdownNow?
参考答案
- 让负载均衡停止派发新流量。
- 温和停止 vs 尝试中断在途。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| Runtime.addShutdownHook | API | 钩子 |