必须配置server.shutdown=graceful启用优雅停机,仅对嵌入式容器生效;需配合actuator暴露/shutdown端点或正确传递sigterm信号;tomcat超时时间在3.2+用server.tomcat.shutdown-wait-timeout,2.3–3.1需代码注入setshutdownwaittime。

Spring Boot 2.3+ 如何启用优雅停机
必须用 server.shutdown=graceful,且仅对嵌入式 Web 容器(Tomcat、Jetty、Netty)生效。低于 2.3 的版本不支持该配置,强行加了也没用。
启用后,收到 SIGTERM(如 kill -15)或调用 Actuator 的 /actuator/shutdown 端点时,容器不再立即关闭连接,而是先拒绝新请求,再等待已有请求完成。
- 必须配合
spring-boot-starter-actuator才能触发/actuator/shutdown(默认禁用,需显式开放) - Tomcat 默认等待 30 秒,超时后强制终止;这个值可调,但不是靠
server.shutdown控制 - 若应用没暴露 Actuator 端点,又没走系统信号(比如在 Docker 中未正确传递 SIGTERM),优雅停机根本不会启动
如何设置优雅停机超时时间
超时由具体容器实现决定,不能统一用一个配置项。Tomcat 用 server.tomcat.connection-timeout 不起作用——那是处理请求的超时,和停机无关。
真正控制等待上限的是:
- Tomcat:配置
server.tomcat.max-connections=0+server.tomcat.connection-timeout也不行,得用server.tomcat.shutdown-wait-timeout(Spring Boot 3.2+)或低版本的server.tomcat.background-processor-delay?错。实际是server.tomcat.max-keep-alive-requests和线程池行为间接影响,但**唯一可靠方式是设server.tomcat.shutdown-wait-timeout(3.2+)或降级到management.endpoint.shutdown.timeout(旧版)?也不对**——真相是:Spring Boot 2.3–3.1 中,**只能通过server.tomcat.shutdown-wait-timeout(注意拼写)**,但它不是官方文档公开属性,属于 TomcatConnectorCustomizer 内部行为,稳妥做法是直接操作 Connector:
@Configuration
public class GracefulShutdownConfig {
@Bean
public WebServerFactoryCustomizer<tomcatservletwebserverfactory> gracefulShutdown() {
return factory -> factory.addAdditionalTomcatConnectors(
new Connector("org.apache.coyote.http11.Http11NioProtocol") {{
setPort(-1); // 占位,不启用
setProperty("connectionTimeout", "10000");
}}
);
// 实际应定制 shutdownWaitTime,但 Tomcat 9+ 没公开 setter
// 正确姿势:用 TomcatServletWebServerFactory.setShutdownWaitTime(Duration.ofSeconds(60))
}
}
</tomcatservletwebserverfactory>
更简单:Spring Boot 3.2+ 直接写 server.tomcat.shutdown-wait-timeout=60s;2.3–3.1 则必须代码注入 TomcatServletWebServerFactory 并调用 setShutdownWaitTime。
为什么 /actuator/shutdown 不生效或报 404
不是配置漏了,就是 Actuator 没暴露对应端点。默认只开 health 和 info,shutdown 是敏感操作,必须手动放开。
- application.yml 中要加:
management.endpoints.web.exposure.include=health,info,shutdown - 还得确保
management.endpoint.shutdown.enabled=true(默认 true,但可能被 profile 覆盖) - 调用时必须是 POST,且带 Content-Type: application/json,空 body 即可;用 GET 或 curl 不带 -X POST 会 405
- 如果启用了 Spring Security,/actuator/shutdown 默认需要 ACTUATOR 角色,否则 401 —— 不是权限配错了,是根本没登录或 token 缺失
优雅停机失败的典型现象和排查点
常见症状:发 kill -15 后进程秒退、日志里看不到 “Waiting for active requests to complete…”、HTTP 连接被 RST 中断。
- Docker 容器中:没加
stop_signal: SIGTERM,或者用kill -9测试——后者永远不触发优雅逻辑 - Kubernetes:Pod terminationGracePeriodSeconds 小于应用设置的 shutdown wait time,K8s 强杀在前,应用还没等完
- 应用里有非守护线程(比如自己 new Thread().start() 且没设 setDaemon(true)),JVM 不会退出,优雅停机卡住
- @PreDestroy 方法阻塞或抛异常,导致 ContextClosedEvent 无法广播,部分组件收不到通知,连接池、消息消费者等可能继续处理老请求
- Netty 响应体流式写入(如 SseEmitter、Flux 返回)未完成时,连接不会被判定为“已完成”,容易超时被强关
最常被忽略的是:以为配了 server.shutdown=graceful 就万事大吉,但没验证信号是否真正到达 JVM,也没确认容器或编排平台有没有覆盖或拦截 SIGTERM。










