核心思路

优雅停机 = 停止接收新请求 → 等待/取消进行中的请求 → 释放资源 → 退出进程。


1. 先停入口,再清资源

阶段 做什么

注册关闭钩子

Runtime.addShutdownHook 或 Spring 的 SmartLifecycle / @PreDestroy

停止接收新流量

从注册中心下线、K8s preStop、负载均衡摘除

关闭 HTTP 容器

停止 accept 新连接,等待已有连接处理完

关闭线程池

shutdown() → awaitTermination() → 必要时 shutdownNow()

关闭 DB/消息队列

连接池 close(),MQ consumer 停止拉取

顺序很重要:先断流量,再关业务,最后关基础设施。


2. 取消正在处理的 HTTP 请求

Spring Boot(内嵌 Tomcat / Jetty / Undertow)

server:
    shutdown: graceful # Spring Boot 2.3+

spring:
    lifecycle:
        timeout-per-shutdown-phase: 30s

开启后,收到 SIGTERM 时:

  1. 不再接受新 HTTP 请求
  2. 已有请求继续执行,直到完成或超时
  3. 超时后强制关闭

手动取消进行中的请求

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()

更多推荐