SpringBoot 优雅停服

一、介绍

当外部流量请求到服务端接口执行业务逻辑的时候,若服务端此时执行关机 (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 启动流程

  1. 自动配置入口

通过 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);
    }
}
  1. 核心组件职责
组件 作用
TomcatWebServerFactoryCustomizer ServerProperties 中的配置应用到 Tomcat 容器
WebServerFactoryCustomizerBeanPostProcessor 后置处理器,在 Bean 初始化前获取所有定制器并应用到 Factory
ServletWebServerApplicationContext 重写 onRefresh() 方法,调用 createWebServer() 创建容器
TomcatServletWebServerFactory 负责实际创建 Tomcat 实例并获取 WebServer
  1. 启动调用链

    SpringApplication.run()

    AbstractApplicationContext.refresh()

    ServletWebServerApplicationContext.onRefresh() [模板方法]

    createWebServer() → Factory.getWebServer() → Tomcat 启动


优雅停机机制(Graceful Shutdown)

触发阶段:

  1. 接收信号kill -15 (SIGTERM),不是 kill -9( kill -9,暴力美学强制杀死进程,不会执行 ShutdownHook****

  2. JVM ShutdownHookSpringApplicationShutdownHookrun() 前注册到 JVM

  3. 关闭上下文 :调用 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
    }
}

执行顺序:

  1. 暂停 Connector → 不再接收新 HTTP 请求

  2. 等待活跃请求 → 在 timeout-per-shutdown-phase 时间内完成

  3. 超时处理 → 超时强制关闭剩余连接

  4. Bean 销毁 → 触发 @PreDestroyDisposableBean

内嵌 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 等方法来完成容器的启停操作。

相关推荐
J2虾虾5 小时前
在SpringBoot中使用Druid
java·spring boot·后端·druid
qwert10377 小时前
跨域问题解释及前后端解决方案(SpringBoot)
spring boot·后端·okhttp
码喽7号7 小时前
springboot学习四:RESTful风格+swagger
spring boot·学习·restful
beata9 小时前
Spring Boot基础-2:Spring Boot 3.x 起步依赖(Starter)深度拆解:为什么引入一个依赖就够了?
spring boot·后端
深蓝轨迹9 小时前
SpringBoot YAML配置文件全解析:语法+读取+高级用法
java·spring boot·后端·学习
深蓝轨迹9 小时前
乐观锁 vs 悲观锁 含面试模板
java·spring boot·笔记·后端·学习·mysql·面试
tant1an12 小时前
Spring Boot 基础入门:从核心配置到 SSMP 整合实战
java·数据库·spring boot·sql·spring
彭于晏Yan13 小时前
Springboot实现微服务监控
spring boot·后端·微服务
小江的记录本13 小时前
【Spring Boot—— .yml(YAML)】Spring Boot中.yml文件的基础语法、高级特性、实践技巧
xml·java·spring boot·后端·spring·spring cloud·架构
稻草猫.14 小时前
SpringBoot日志全解析:从调试到持久化
java·开发语言·spring boot·java-ee·idea