发布于2026-07-17 阅读(0)
扫一扫,手机访问
在微服务和云原生架构日益普及的今天,应用的平滑启停能力,早已成为保障系统高可用的关键环节。而 Spring Boot 在 2.3 版本中引入了一项非常重要的更新——对内嵌 Web 容器的优雅停机提供了原生支持。这彻底改变了此前需要开发者手动编码实现这一功能的局面,可以说是一次不小的进步。

所谓优雅停机(Graceful Shutdown),核心目标是在应用停止的过程中,尽可能避免对正在处理的请求造成影响。具体来说,需要做到以下四点:
如果没有实现优雅停机,在滚动更新、缩容或重启时,很容易出现下面这种典型的异常:
ERROR ... - Error creating bean with name 'orderController': Singleton bean creation not allowed while singletons of this factory are in destruction org.springframework.beans.factory.BeanCreationNotAllowedException: ...
这个异常信息其实很直白:Spring 容器已经在销毁了,但此时仍有请求试图获取 Controller Bean。这恰恰是停机流程设计不当的直接后果。
要理解优雅停机,首先得搞清楚各个组件之间的“居住”关系:
JVM
└── 内嵌 Web 容器(如 Tomcat)
└── Servlet 容器(StandardContext)
└── DispatcherServlet(Spring MVC 入口)
└── Spring ApplicationContext(IoC 容器)
└── 所有 Spring Bean(@Controller, @Service 等)
ApplicationContext,管理所有 Bean 的创建与销毁。⚠️ 关键点:Spring 容器是运行在 Web 容器内部的。停机时,必须协调好二者的关闭顺序,否则就会出现“Web 容器还在处理请求,但 Spring 容器已经拒绝服务”的竞态条件。
在 2.3 版本之前,Spring Boot 并没有内置的优雅停机能力。它的默认行为是什么样的呢?
SIGTERM 信号(比如 kill -15);singletonsCurrentlyInDestruction = true;DispatcherServlet 尝试通过 getBean("orderController") 获取 Controller;BeanCreationNotAllowedException。先关 Spring,再关 Web 容器 → 中间存在一个非常危险的“时间窗口”。
当时,开发者们不得不手动实现一套逻辑来解决问题:
@Component
public class GracefulShutdown implements TomcatConnectorCustomizer,
ApplicationListener {
private volatile Connector connector;
@Override
public void customize(Connector connector) {
this.connector = connector;
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
connector.pause(); // 停止接收新请求
Executor executor = connector.getProtocolHandler().getExecutor();
if (executor instanceof ThreadPoolExecutor) {
try {
((ThreadPoolExecutor) executor).shutdown();
((ThreadPoolExecutor) executor).awaitTermination(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
同时,还需要将这个自定义组件注册到 Tomcat 中:
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.addConnectorCustomizers(gracefulShutdown());
return tomcat;
}
? 但即便这样,也无法完美处理 HTTP/1.1 keep-alive 连接复用场景下带来的新请求。
到了 Spring Boot 2.3,事情就简单多了。官方引入了 标准化的优雅停机机制,只需要两行配置就能搞定:
server:
shutdown: graceful # 启用优雅停机
spring:
lifecycle:
timeout-per-shutdown-phase: 30s # 等待超时时间
现在的停机流程变成了这样:
connector.pause());timeout-per-shutdown-phase 设定的时间;Spring Boot 注册了一个 WebServerGracefulShutdownLifecycle,它实现了 SmartLifecycle 接口,并设置了:
@Override
public int getPhase() {
return Integer.MAX_VALUE; // 最后启动,最先停止
}
这就保证了它的 stop() 方法会在普通 Bean 销毁之前执行。
不同的内嵌服务器现在都实现了统一的 GracefulShutdown 行为:
pause() + 等待线程池通过 LifecycleProcessor 来协调所有 SmartLifecycle Bean 的关闭顺序,确保 Web 容器优雅关闭完成之后,才触发 ApplicationContext.close()。
| 维度 | Spring Boot < 2.3 | Spring Boot ≥ 2.3 |
|---|---|---|
| 是否内置支持 | ❌ 需手动编码 | ✅ 开箱即用 |
| 关闭起点 | Spring 容器 | Web 容器 |
| 新请求处理 | 继续 accept → 高风险 | 立即拒绝(pause/503)→ 安全 |
| 已进入请求 | 可能因 Spring 关闭而失败 | 等待完成(最多 timeout) |
| 典型异常 | BeanCreationNotAllowedException 高频 |
极少(除非超时) |
| 配置复杂度 | 高(需自定义监听器) | 低(仅需 YAML) |
| 多容器支持 | 仅限手动适配 | 自动支持 Tomcat/Jetty/Undertow/Netty |
| K8s 友好度 | 差 | 优秀 |
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 45s # 建议大于业务最大请求耗时
spec:
containers:
- name: app
lifecycle:
preStop:
exec:
command: ["sleep", "15"] # 给 LB 时间摘除节点
terminationGracePeriodSeconds: 60
management:
endpoint:
shutdown:
enabled: true
endpoints:
web:
exposure:
include: "shutdown"
然后通过外部命令触发:
curl -X POST http://localhost:8080/actuator/shutdown
5xx 错误率;BeanCreationNotAllowedException,可以从这几个方面排查:? 一句话建议:
只要你的 Spring Boot 版本 ≥ 2.3,务必启用
server.shutdown=graceful,并配合基础设施实现流量摘除。这能显著提升系统在滚动更新、弹性伸缩场景下的稳定性与用户体验。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8