JAVA笔记之优雅停机
核心思路
优雅停机 = 停止接收新请求 → 等待/取消进行中的请求 → 释放资源 → 退出进程。
1. 先停入口,再清资源
| 阶段 | 做什么 |
|---|---|
|
注册关闭钩子 |
|
|
停止接收新流量 |
从注册中心下线、K8s |
|
关闭 HTTP 容器 |
停止 accept 新连接,等待已有连接处理完 |
|
关闭线程池 |
|
|
关闭 DB/消息队列 |
连接池 |
顺序很重要:先断流量,再关业务,最后关基础设施。
2. 取消正在处理的 HTTP 请求
Spring Boot(内嵌 Tomcat / Jetty / Undertow)
server:
shutdown: graceful # Spring Boot 2.3+
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
开启后,收到 SIGTERM 时:
- 不再接受新 HTTP 请求
- 已有请求继续执行,直到完成或超时
- 超时后强制关闭
手动取消进行中的请求
Tomcat 思路:关闭 Connector → 已有请求在 worker 线程里跑完或超时。
若要主动中断:
// 1. 请求线程可响应中断
while (!Thread.currentThread().isInterrupted()) {
// 业务逻辑
}
// 2. 关闭 Connector 后,对仍在执行的 worker 线程 interrupt
connector.pause();
connector.getProtocolHandler().close();
executor.shutdownNow(); // 会向 worker 线程发 interrupt
Spring MVC / WebFlux:
- Servlet(阻塞):依赖容器线程池 + 超时;业务代码需检查
Thread.interrupted(),IO 操作要用可中断 API。 - WebFlux(响应式):取消 =
Subscription.cancel();客户端断开或 shutdown 时会传播 cancel signal,需在doOnCancel/takeUntilOther里释放资源。
通用做法
// 全局“停机中”标志 + 请求拦截
@Component
public class ShutdownFilter implements Filter {
private volatile boolean shuttingDown = false;
@EventListener
public void onShutdown(ContextClosedEvent e) {
shuttingDown = true;
}
@Override
public void doFilter(...) {
if(shuttingDown){
response.setStatus(503);
return; // 拒绝新请求
}
chain.doFilter(...);
}
}
对新请求返回 503;已在处理的请求靠容器 grace period 自然结束。
3. 关键配置(生产环境)
# K8s 示例
lifecycle:
preStop:
exec:
command: ["sleep", "5"] # 等 LB 摘除后再 SIGTERM
terminationGracePeriodSeconds: 60 # 要大于应用 shutdown 超时
原则:preStop 延迟 + 应用 grace timeout < K8s terminationGracePeriodSeconds
4. 业务层必须配合
- 长任务:可中断设计,定期检查 shutdown 标志或
Thread.interrupted() - 异步任务:用有界队列 +
shutdownNow(),并处理InterruptedException - 分布式事务/幂等:被中断的请求可能已部分成功,需幂等或补偿
- Feign / RestTemplate:配置 connect/read timeout,避免 hang 住 shutdown
5. 总结
优雅停机 = 注册中心/LB 先摘流量 +
server.shutdown=graceful等容器请求 drain + 线程池awaitTermination+ 业务代码可中断;取消进行中的 HTTP 请求靠容器 grace period 自然结束,或shutdownNow()中断 worker 线程,响应式栈用Subscription.cancel()。
更多推荐

所有评论(0)