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

相关推荐
思想在飞肢体在追3 小时前
Springboot项目配置Nacos
java·spring boot·后端·nacos
JavaGuide5 小时前
推荐一个基于 Spring Boot 4.0 + Java 21 + Spring AI 2.0 的大模型项目!
java·spring boot·spring
小马爱打代码6 小时前
Spring Boot :使用 Spring Cache 注解方式集成 Redis
spring boot·redis·spring
东东5167 小时前
果园预售系统的设计与实现spingboot+vue
前端·javascript·vue.js·spring boot·个人开发
不光头强7 小时前
spring boot项目欢迎页设置方式
java·spring boot·后端
怪兽毕设7 小时前
基于SpringBoot的选课调查系统
java·vue.js·spring boot·后端·node.js·选课调查系统
学IT的周星星7 小时前
Spring Boot Web 开发实战:第二天,从零搭个“会卖萌”的小项目
spring boot·后端·tomcat
笨蛋不要掉眼泪8 小时前
Spring Boot + RedisTemplate 数据结构的基础操作
java·数据结构·spring boot·redis·wpf
编程彩机8 小时前
互联网大厂Java面试:从分布式事务到微服务优化的技术场景解读
java·spring boot·redis·微服务·面试·kafka·分布式事务