一、介绍

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

2. 核心组件职责

组件 作用
TomcatWebServerFactoryCustomizer 将 ServerProperties 中的配置应用到 Tomcat 容器
WebServerFactoryCustomizerBeanPostProcessor 后置处理器,在 Bean 初始化前获取所有定制器并应用到 Factory
ServletWebServerApplicationContext 重写 onRefresh() 方法,调用 createWebServer() 创建容器
TomcatServletWebServerFactory 负责实际创建 Tomcat 实例并获取 WebServer

3. 启动调用链

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

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐