springBoot如何做到优雅停机的?

知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!


Spring Boot 优雅停机(Graceful Shutdown)实现指南

在分布式系统中,服务的优雅停机(Graceful Shutdown)是确保业务连续性的重要机制。Spring Boot 通过 内嵌服务器支持生命周期管理 实现了这一功能,以下是具体实现方式和配置细节:

1. 优雅停机的核心目标

  • 停止接收新请求:关闭监听端口,不再接受新连接。
  • 等待处理中的请求完成:允许正在执行的请求继续处理直至超时。
  • 释放资源:关闭数据库连接池、线程池等资源。
  • 避免数据不一致:确保事务完整性,防止中途中断。

2. 基于内嵌服务器的优雅停机配置

Spring Boot 从 2.3 版本 开始,为 Tomcat、Jetty、Undertow 等内嵌服务器内置了优雅停机支持。

2.1 启用优雅停机

application.propertiesapplication.yml 中配置:

yaml 复制代码
server:
  shutdown: graceful  # 启用优雅停机模式
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s  # 等待请求完成的超时时间(默认30秒)
2.2 不同服务器的行为
服务器 优雅停机行为
Tomcat 停止接收新请求,等待处理中的请求完成,超时后强制关闭。
Jetty 停止接收新连接,等待活跃请求完成。
Undertow 关闭监听端口,等待活跃请求完成。
2.3 验证停机流程
  1. 发送停机信号

    • 通过 SIGTERM 信号(Kill 命令)或 Actuator 端点关闭应用。

    • 使用 Actuator 端点需要添加依赖和配置:

      xml 复制代码
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
      yaml 复制代码
      management:
        endpoint:
          shutdown:
            enabled: true
        endpoints:
          web:
            exposure:
              include: shutdown
    • 发送 POST 请求关闭应用:

      bash 复制代码
      curl -X POST http://localhost:8080/actuator/shutdown
  2. 观察日志

    arduino 复制代码
    2023-10-05 14:20:00 INFO  o.s.b.w.e.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete
    2023-10-05 14:20:30 INFO  o.s.b.w.e.tomcat.GracefulShutdown : Graceful shutdown complete

3. 自定义停机逻辑扩展

3.1 监听停机事件

通过实现 ApplicationListener<ContextClosedEvent> 监听上下文关闭事件:

java 复制代码
@Component
public class GracefulShutdownListener implements ApplicationListener<ContextClosedEvent> {
    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        //dubbo关闭
        DubboBootstrap.getInstance().stop();
        // 执行自定义清理逻辑(如关闭线程池、释放资源)
        ExecutorService executor = event.getApplicationContext().getBean(ExecutorService.class);
        executor.shutdown();
        try {
            if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
3.2 使用 @PreDestroy 注解

在 Bean 销毁前执行清理逻辑:

java 复制代码
@Service
public class DatabaseService {
    @PreDestroy
    public void cleanup() {
        // 关闭数据库连接
        DataSource dataSource = ...;
        dataSource.close();
    }
}

4. 集成 Kubernetes 的优雅停机

在 Kubernetes 中,结合 Pod 生命周期钩子 实现优雅停机:

4.1 配置 PreStop Hook

在 Deployment 配置中添加 preStop 钩子,延迟终止进程:

yaml 复制代码
spec:
  template:
    spec:
      containers:
        - name: app
          lifecycle:
            preStop:
              exec:
                command: ["sh", "-c", "sleep 30"]  # 等待30秒后发送SIGTERM
          terminationGracePeriodSeconds: 60  # 总宽限期(包括preStop)
4.2 配合就绪探针

在停机前标记服务不可用:

yaml 复制代码
readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080

5. 常见问题与解决方案

问题 解决方案
停机超时后仍有未完成请求 调整 spring.lifecycle.timeout-per-shutdown-phase 延长等待时间。
线程池任务未执行完 自定义 ExecutorService 并在停机时调用 shutdown()awaitTermination()
数据库连接未关闭 通过 @PreDestroyDisposableBean 显式关闭连接池。
Kubernetes 强制终止 Pod 确保 terminationGracePeriodSeconds 大于优雅停机超时时间。

6. 验证优雅停机效果

  1. 发送长耗时请求

    bash 复制代码
    curl http://localhost:8080/long-task
  2. 触发停机

    bash 复制代码
    curl -X POST http://localhost:8080/actuator/shutdown
  3. 观察结果

    • 新请求返回 503 Service Unavailable
    • 正在处理的请求继续执行直至完成或超时。

总结

通过 内嵌服务器配置生命周期钩子Kubernetes 集成,Spring Boot 实现了开箱即用的优雅停机功能。关键配置包括:

  1. 启用 server.shutdown=graceful
  2. 设置合理的超时时间
  3. 结合 Actuator 端点或 Kubernetes 生命周期管理
相关推荐
Hooray7 分钟前
2026年,站在职业生涯十字路口的我该何去何从?
前端·后端
唐叔在学习12 分钟前
还在申请云服务器来传输数据嘛?试试P2P直连吧
后端·python
week_泽20 分钟前
第5课:短期记忆与长期记忆原理 - 学习笔记_5
java·笔记·学习·ai agent
像风一样自由27 分钟前
android native 中的函数动态注册方式总结
android·java·服务器·安卓逆向分析·native函数动态注册·.so文件分析
兮动人40 分钟前
Maven指定加载的类
java·maven·maven指定加载的类
开心猴爷42 分钟前
iOS 代码混淆在项目中的方式, IPA 级保护实践记录
后端
xlp666hub1 小时前
Linux 设备模型学习笔记(1)
面试·嵌入式
魅影骑士00101 小时前
柯里化函数
后端·设计模式
wangkay881 小时前
【Java 转运营】Day04:抖音新号起号前准备全指南
java·开发语言·新媒体运营
JOEH601 小时前
🛡️ 微服务雪崩救星:Sentinel 限流熔断实战,3行代码搞定高可用!
后端·全栈