SpringBoot 优雅停机演进:实现原理与最佳实践
❃博主首页 :「程序员1970」 ,同名公众号「程序员1970」
☠博主专栏 :<mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关>
微服务和云原生时代,应用的平滑启停是保障系统高可用的关键环节。Spring Boot 在 2.3 版本引入了对内嵌 Web 容器优雅停机的原生支持,改变此前需要手动编码实现局面。
一、什么是“优雅停机”?
优雅停机(Graceful Shutdown) 是指在应用停止过程中:
- 立即停止接收新请求;
- 允许已进入的请求继续处理完成;
- 安全释放资源(数据库连接、线程池、缓存等);
- 避免客户端收到 5xx 错误或连接中断。
若未实现优雅停机,在滚动更新、缩容或重启时,极易出现如下典型异常:
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 等) - Web 容器(Tomcat/Jetty/Undertow):负责网络连接与线程调度。
- Servlet 容器:管理 Servlet、Filter 生命周期。
- Spring 容器:即
ApplicationContext,管理 Bean 的创建与销毁。 - Spring Boot:不是容器,而是自动集成 Web 容器 + Spring 容器的启动框架。
⚠️ 关键点:Spring 容器运行在 Web 容器内部。停机时,必须协调二者关闭顺序,否则就会出现“Web 容器还在处理请求,但 Spring 容器已拒绝服务”的竞态条件。
三、Spring Boot < 2.3:手动实现的“伪优雅”
在2.3之前,Spring Boot 没有内置优雅停机能力。默认行为是:
- 收到
SIGTERM(如kill -15); - 立即关闭 Spring 容器 → 设置
singletonsCurrentlyInDestruction = true; - Web 容器仍在运行,继续 accept 新连接并分配线程;
- 若此时有请求进入,
DispatcherServlet尝试通过getBean("orderController")获取 Controller; - 因 Spring 容器已标记为“销毁中”,抛出
BeanCreationNotAllowedException。
❌ 问题本质:关闭顺序错误
先关 Spring,再关 Web 容器 → 存在危险时间窗口。
🛠️ 开发者workaround
需手动实现以下逻辑:
@ComponentpublicclassGracefulShutdownimplementsTomcatConnectorCustomizer,ApplicationListener<ContextClosedEvent>{privatevolatileConnector connector;@Overridepublicvoidcustomize(Connector connector){this.connector = connector;}@OverridepublicvoidonApplicationEvent(ContextClosedEvent event){ connector.pause();// 停止接收新请求Executor executor = connector.getProtocolHandler().getExecutor();if(executor instanceofThreadPoolExecutor){try{((ThreadPoolExecutor) executor).shutdown();((ThreadPoolExecutor) executor).awaitTermination(30,TimeUnit.SECONDS);}catch(InterruptedException e){Thread.currentThread().interrupt();}}}}同时需注册到 Tomcat:
@BeanpublicServletWebServerFactoryservletContainer(){TomcatServletWebServerFactory tomcat =newTomcatServletWebServerFactory(); tomcat.addConnectorCustomizers(gracefulShutdown());return tomcat;}💡 即便如此,仍无法完美处理 HTTP/1.1 keep-alive 连接复用带来的新请求。
四、Spring Boot ≥ 2.3:原生优雅停机
Spring Boot 2.3 引入了 标准化的优雅停机机制,通过两行配置即可启用:
server:shutdown: graceful # 启用优雅停机spring:lifecycle:timeout-per-shutdown-phase: 30s # 等待超时时间✅ 核心思想:反转关闭顺序 + 流量控制前置
停机流程变为:
- Web 容器立即 pause:停止 accept 新连接(Tomcat 调用
connector.pause()); - 等待已进入的请求完成:最多等待
timeout-per-shutdown-phase时间; - 确认无活跃请求后,关闭 Spring 容器:销毁所有 Bean;
- 彻底停止 Web 容器线程池。
🧠 底层原理
1. SmartLifecycle 控制关闭阶段
Spring Boot 注册了 WebServerGracefulShutdownLifecycle,它实现 SmartLifecycle 接口,并设置:
@OverridepublicintgetPhase(){returnInteger.MAX_VALUE;// 最后启动,最先停止}确保其 stop() 方法在普通 Bean 销毁之前执行。
2. WebServer 接口标准化
不同内嵌服务器实现统一的 GracefulShutdown 行为:
- Tomcat:
pause()+ 等待线程池 - Jetty:停止 acceptor 线程
- Undertow:返回 503 给新请求
- Reactor Netty:发送 GOAWAY(HTTP/2)
3. 与 Spring 生命周期深度集成
通过 LifecycleProcessor 协调所有 SmartLifecycle Bean 的关闭顺序,确保 Web 容器优雅关闭完成之后,才触发 ApplicationContext.close()。
五、2.3 前后对比总表
| 维度 | Spring Boot < 2.3 | Spring Boot ≥ 2.3 |
|---|---|---|
| 是否内置支持 | ❌ 需手动编码 | ✅ 开箱即用 |
| 关闭起点 | Spring 容器 | Web 容器 |
| 新请求处理 | 继续 accept → 高风险 | 立即拒绝(pause/503)→ 安全 |
| 已进入请求 | 可能因 Spring 关闭而失败 | 等待完成(最多 timeout) |
| 典型异常 | BeanCreationNotAllowedException 高频 | 极少(除非超时) |
| 配置复杂度 | 高(需自定义监听器) | 低(仅需 YAML) |
| 多容器支持 | 仅限手动适配 | 自动支持 Tomcat/Jetty/Undertow/Netty |
| K8s 友好度 | 差 | 优秀 |
六、生产环境最佳实践
1. 启用优雅停机(2.3+)
server:shutdown: graceful spring:lifecycle:timeout-per-shutdown-phase: 45s # > 业务最大请求耗时2. Kubernetes 部署配合 preStop
spec:containers:-name: app lifecycle:preStop:exec:command:["sleep","15"]# 给 LB 时间摘除节点terminationGracePeriodSeconds:603. Windows 服务需走 Actuator
management:endpoint:shutdown:enabled:trueendpoints:web:exposure:include:"shutdown"并通过外部命令触发:
curl-X POST http://localhost:8080/actuator/shutdown 4. 监控与告警
- 监控停机期间的
5xx错误率; - 若仍有
BeanCreationNotAllowedException,检查:- 超时时间是否足够;
- LB 摘除延迟;
- 是否存在长轮询/WebSocket 未处理。
🌟 一句话建议:
只要你的Spring Boot 版本 ≥ 2.3,务必启用server.shutdown=graceful,并配合基础设施实现流量摘除。这能显著提升系统在滚动更新、弹性伸缩场景下的稳定性与用户体验。
关注公众号获取更多技术干货 !