当前位置: 首页 > news >正文

SpringBoot优雅关机

1. 启用优雅关机

在 application.yml 中启用优雅关机,并设置最大等待时间:

server:shutdown: graceful # 启用优雅关机
spring:lifecycle:timeout-per-shutdown-phase: 30s # 设置优雅关机的最大等待时间
2. 创建一个长时间运行的任务

编写一个简单的服务类,模拟一个阻塞的任务:

import org.springframework.stereotype.Service;@Service
public class LongRunningTaskService {public void executeLongRunningTask() {System.out.println("Long running task started at: " + System.currentTimeMillis());try {// 模拟任务执行 10 秒Thread.sleep(10000);} catch (InterruptedException e) {Thread.currentThread().interrupt();System.err.println("Long running task was interrupted.");}System.out.println("Long running task finished at: " + System.currentTimeMillis());}
}
3. 创建控制器触发任务

编写一个控制器,用于手动触发上述任务:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class TaskController {private final LongRunningTaskService longRunningTaskService;public TaskController(LongRunningTaskService longRunningTaskService) {this.longRunningTaskService = longRunningTaskService;}@GetMapping("/start-task")public String startTask() {new Thread(longRunningTaskService::executeLongRunningTask).start();return "Task started successfully.";}
}

一、通过监听 ContextClosedEvent

import org.quartz.Scheduler;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;@Component
public class QuartzGracefulShutdown implements ApplicationListener<ContextClosedEvent> {private final Scheduler scheduler;public QuartzGracefulShutdown(Scheduler scheduler) {this.scheduler = scheduler;}@Overridepublic void onApplicationEvent(ContextClosedEvent event) {System.out.println("Starting graceful shutdown for Quartz...");try {// 关闭 Quartz 定时任务scheduler.shutdown(true); // true 表示等待正在运行的任务完成// 检查 Quartz 是否已关闭while (!scheduler.isShutdown()) {System.out.println("Waiting for Quartz tasks to shut down...");Thread.sleep(1000); // 每秒检查一次}System.out.println("All Quartz tasks have been shut down.");} catch (Exception e) {System.err.println("Failed to gracefully shut down Quartz: " + e.getMessage());}}
}

二、@PreDestroy

@PreDestroy 是 Java 的标准注解,用于标记一个方法在 Bean 被销毁之前执行。它通常用于释放资源或执行清理逻辑。然而,在 Spring Boot 的优雅关机场景中,使用 @PreDestroy 是否合适需要具体分析。

以下是关键点:

  1. Spring 生命周期与 @PreDestroy

    • 当 Spring 容器关闭时,所有被管理的 Bean 都会被销毁,@PreDestroy 注解的方法会在销毁前调用。
    • 但是,@PreDestroy 方法的执行时机是在容器关闭过程中,并不保证与 Spring Boot 的优雅关机机制(如 HTTP 请求的优雅处理或线程池的等待)完全同步。
  2. 适用场景

    • 如果您的目标是关闭一些资源(如定时任务、数据库连接等),@PreDestroy 是可以实现的。
    • 但如果需要更复杂的逻辑(如阻塞等待任务完成),@PreDestroy 可能不够灵活。

使用 @PreDestroy 实现优雅关机

1. 基本实现

我们可以通过 @PreDestroy 注解的方法来关闭 Quartz 定时任务,并检查是否关闭成功。

import javax.annotation.PreDestroy;
import org.springframework.stereotype.Component;@Component
public class QuartzShutdownHandler {@PreDestroypublic void shutdown() {System.out.println("Starting graceful shutdown via @PreDestroy...");}
}

在 Spring Boot 中,ContextClosedEvent 和 @PreDestroy 都可以用于在应用程序关闭时执行清理逻辑。然而,它们的作用范围、触发时机和使用场景有所不同。以下是两者的详细对比和适用场景分析。


1. ContextClosedEvent

定义
  • ContextClosedEvent 是 Spring 的生命周期事件之一,当 Spring 容器关闭时触发。
  • 它是 Spring 的事件机制的一部分,可以通过实现 ApplicationListener<ContextClosedEvent> 或使用 @EventListener 注解来监听。
触发时机
  • 在 Spring 容器关闭的过程中触发,具体是在 ApplicationContext 调用 close() 方法后。
  • 触发顺序早于 @PreDestroy,因为它是容器级别的事件。
特点
  • 全局性:适用于需要在整个应用程序范围内执行清理逻辑的场景。
  • 灵活性:可以通过监听多个事件(如 ContextRefreshedEventContextStartedEvent 等)实现更复杂的逻辑。
  • 可扩展性:可以结合其他 Spring 组件(如线程池、定时任务等)实现优雅关机。
示例代码
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;@Component
public class ShutdownListener implements ApplicationListener<ContextClosedEvent> {@Overridepublic void onApplicationEvent(ContextClosedEvent event) {System.out.println("ContextClosedEvent triggered: Shutting down...");// 执行清理逻辑,例如关闭线程池或定时任务}
}

2. @PreDestroy

定义
  • @PreDestroy 是 Java 标准注解,用于标记一个方法在 Bean 被销毁之前执行。
  • 它是 JSR-250 规范的一部分,Spring 支持该注解。
触发时机
  • 在 Spring 容器关闭时,针对每个被管理的 Bean,调用其标注了 @PreDestroy 的方法。
  • 触发顺序晚于 ContextClosedEvent,因为它是 Bean 级别的销毁逻辑。
特点
  • 局部性:适用于单个 Bean 的清理逻辑,例如释放资源(数据库连接、文件句柄等)。
  • 简单性:无需额外配置,直接在 Bean 的方法上添加注解即可。
  • 依赖性:只能用于 Spring 管理的 Bean,无法处理非 Spring 管理的组件。
示例代码
import javax.annotation.PreDestroy;
import org.springframework.stereotype.Component;@Component
public class ResourceCleanup {@PreDestroypublic void cleanup() {System.out.println("@PreDestroy triggered: Cleaning up resources...");// 执行清理逻辑,例如关闭文件或释放内存}
}

3. 对比总结

特性ContextClosedEvent@PreDestroy
触发时机Spring 容器关闭时,早于 @PreDestroyBean 销毁时,晚于 ContextClosedEvent
作用范围全局(整个应用程序上下文)局部(单个 Bean)
灵活性高,可以监听多个事件并执行复杂逻辑低,仅限于单个 Bean 的清理逻辑
适用场景优雅关机、关闭线程池、等待任务完成等全局操作关闭资源、释放文件句柄等局部操作
依赖性不依赖特定 Bean,适用于全局逻辑必须是 Spring 管理的 Bean
扩展性可以结合其他 Spring 功能(如事件机制)仅限于简单的销毁逻辑

4. 使用场景推荐

适合使用 ContextClosedEvent 的场景
  1. 优雅关机

    • 需要等待线程池中的任务完成。
    • 需要关闭 Quartz 定时任务或其他后台线程。
    • 示例:等待所有 HTTP 请求完成后再关闭服务。
  2. 全局资源清理

    • 需要在整个应用程序范围内执行清理逻辑。
    • 示例:关闭数据库连接池、释放共享资源。
  3. 多组件协调

    • 需要协调多个 Bean 的关闭顺序。
    • 示例:先关闭 A 组件,再关闭 B 组件。
适合使用 @PreDestroy 的场景
  1. 单个 Bean 的资源释放

    • 需要释放某个 Bean 的独占资源。
    • 示例:关闭文件句柄、释放网络连接。
  2. 轻量级清理逻辑

    • 清理逻辑简单且不需要与其他组件交互。
    • 示例:清除缓存、重置状态。

5. 实际应用中的选择

在实际开发中,可以根据需求选择合适的机制:

  • 如果需要实现优雅关机或全局清理逻辑,优先使用 ContextClosedEvent
  • 如果只需要为单个 Bean 执行简单的清理逻辑,使用 @PreDestroy 更加方便。

注:K8S配置:terminationGracePeriodSeconds: 120

http://www.xdnf.cn/news/239473.html

相关文章:

  • MicroPython 开发ESP32应用教程 之 ADC及应用实例:电池电量检测并显示
  • HarmonyOS NEXT应用开发-Notification Kit(用户通知服务)notificationManager.cancelAll
  • ComfyUI
  • 国标GB28181平台EasyGBS未来研发方向在哪?
  • 数字中国开新篇,数智化为何需要新引擎
  • SLAM中的状态估计理论:从基础到前沿的完整解析
  • C++初阶:类和对象(二)
  • 机器学习|通过线性回归了解算法流程
  • spring 面试题
  • 智能 + 安全:婴幼儿托育管理实训基地标准化建设方案
  • 【LLM】MOE混合专家大模型综述(重要模块原理)
  • AI中常用概念的理解
  • w313安康学院新型冠状病毒肺炎疫情防控专题网站设计与实现
  • 【python实用小脚本-43】用Python自动发送生日祝福,让情感更高效
  • 架构进阶:72页集管IT基础设施蓝图设计方案【附全文阅读】
  • Nautilus侧栏没有桌面
  • 通过Yoast设置SEO标题不生效
  • OpenCV学习笔记(完)
  • Linux -- 操作系统
  • dubbo泛化调用时transient字段失效问题
  • 什么是基尔霍夫第一定律
  • 【python】-基础语法3
  • Semtech公司简介以及主流产品
  • C++继承(下)
  • 【补题】Codeforces Global Round 20 D. Cyclic Rotation
  • Ethan独立开发产品日报 | 2025-04-29
  • 小白学习java第15天(中):javaWeb
  • 高斯-牛顿法与列文伯格-马夸尔特法:非线性优化的理论推导与C++实现
  • Java @Transactional事物隔离级别和默认值详解
  • git did not exit cleanly (exit code 128) 已解决