一、介绍
当外部流量请求到服务端接口执行业务逻辑的时候,若服务端此时执行关机 (kill),spring boot 默认情况会直接关闭容器(tomcat 等),导致此业务逻辑执行失败。在一些业务场景下:会出现数据不一致的情况,事务逻辑不会回滚。
在最新的 spring boot 2.3 版本,内置此功能,不需要再自行扩展容器线程池来处理, 目前 spring boot 嵌入式支持的 web 服务器(Jetty、Reactor Netty、Tomcat 和 Undertow)以及反应式和基于 Servlet 的 web 应用程序都支持优雅停机功能。
持的 shutdown 行为,我们看下源码枚举如下:
public enum Shutdown {
/**
* 优雅停机 (限期停机)
*/
GRACEFUL,
/**
* 立即停机
*/
IMMEDIATE;
}
二、使用
当使用server.shutdown=graceful启用时,在 web 容器关闭时,web 服务器将不再接收新请求,并将等待活动请求完成的缓冲期。
缓冲期 timeout-per-shutdown-phase 配置:默认时间为 30S, 意味着最大等待 30S,超时候无论线程任务是否执行完毕都会停机处理,一定要根据项目实际需要合理设置。
spring:
lifecycle:
timeout-per-shutdown-phase: 10s # 留给Web容器处理存量请求的最大时间
server:
shutdown: graceful
三、源码分析
内置 Tomcat 启动流程
- 自动配置入口
通过 EmbeddedWebServerFactoryCustomizerAutoConfiguration 引入 Tomcat 相关组件:
@Configuration
@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
public static class TomcatWebServerFactoryCustomizerConfiguration {
@Bean
public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(
Environment environment, ServerProperties serverProperties) {
// 创建 Tomcat 配置定制器
return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
}
}
- 核心组件职责
| 组件 | 作用 |
|---|---|
TomcatWebServerFactoryCustomizer |
将 ServerProperties 中的配置应用到 Tomcat 容器 |
WebServerFactoryCustomizerBeanPostProcessor |
后置处理器,在 Bean 初始化前获取所有定制器并应用到 Factory |
ServletWebServerApplicationContext |
重写 onRefresh() 方法,调用 createWebServer() 创建容器 |
TomcatServletWebServerFactory |
负责实际创建 Tomcat 实例并获取 WebServer |
-
启动调用链
SpringApplication.run()
↓
AbstractApplicationContext.refresh()
↓
ServletWebServerApplicationContext.onRefresh() [模板方法]
↓
createWebServer() → Factory.getWebServer() → Tomcat 启动
优雅停机机制(Graceful Shutdown)
触发阶段:
-
接收信号 :
kill -15(SIGTERM),不是kill -9(kill -9,暴力美学强制杀死进程,不会执行 ShutdownHook**)** -
JVM ShutdownHook :
SpringApplicationShutdownHook在run()前注册到 JVM -
关闭上下文 :调用
ApplicationContext.close(),触发ContextClosedEvent
Tomcat 处置阶段(GracefulShutdown):
final class GracefulShutdown {
void shutDownGracefully(GracefulShutdownCallback callback) {
// 1. 暂停 Connector,停止接收新请求
connectors.forEach(this::close);
// 2. 循环等待活动请求完成
while (isActive(context)) {
if (超时或中断) {
callback.shutdownComplete(REQUESTS_ACTIVE); // 强制结束
return;
}
Thread.sleep(50);
}
callback.shutdownComplete(IDLE); // 优雅结束
}
private void close(Connector connector) {
connector.pause(); // 暂停新连接
connector.getProtocolHandler().closeServerSocketGraceful(); // 优雅关闭Socket
}
}
执行顺序:
-
暂停 Connector → 不再接收新 HTTP 请求
-
等待活跃请求 → 在
timeout-per-shutdown-phase时间内完成 -
超时处理 → 超时强制关闭剩余连接
-
Bean 销毁 → 触发
@PreDestroy和DisposableBean
内嵌 Tomcat 如何关闭(优雅停机)-源码分析
当接收到终止信号时,经过一连串的调用,最终会调用到 AbstractApplicationContext#doClose 方法。
AbstractApplicationContext#doClose()
↓
DefaultLifecycleProcessor#onClose
↓
DefaultLifecycleProcessor#stopBeans
↓
DefaultLifecycleProcessor#stop()
↓
DefaultLifecycleProcessor#doStop()
↓
WebServerGracefulShutdownLifecycle#stop()
↓
TomcatWebServer#shutDownGracefully()
↓
WebServerStartStopLifecycle.stop()
↓
TomcatWebServer#stop()
当开启优雅停机时,控制台输出如下,会等待所有请求完成,然后进行 shutdown。
[ionShutdownHook] o.s.b.w.e.tomcat.Gracefulshhutdown : Commencing graceful shutdown. Waiting for activerequests to complete
[tomcat-shutdown] o.s.b.w.e.tomcat.Gracefulshutdown: Graceful shutdown complete
当优雅停机的处理完毕后,接着会处理 WebServerStartStopLifecycle 的 stop 操作,最终会调用 Tomcat API 完成服务的停止。
Spring Boot 内嵌的 Tomcat 启动、关闭的实现方式,大体可以简化成:
内嵌容器启动或关闭时,经过 DefaultLifecycleProcessor 调用一系列方法进行最终是由 WebServerStartStopLifecycle、 WebServerGracefulShutdownLifecycle 来处理。而WebServerStartStopLifecycle、 WebServerGracefulShutdownLifecycle 持有 webServer 对象,最后调用持有的 xxWebServer 对象的 start、stop 等方法来完成容器的启停操作。