You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Spring Boot应用接收系统终止信号后优雅停机?

Spring Boot优雅停机处理系统终止信号方案

首先要明确:强制终止信号(如kill -9、taskkill /f)会直接由操作系统终止进程,JVM没有机会执行任何回调逻辑,所以你的@PreDestroy方法不可能触发。只有优雅终止信号(如kill -15、不带/f的taskkill /pid)才会让JVM收到通知,进而执行停机回调。

问题根源分析

你在Windows下执行taskkill /pid提示必须强制关闭,大概率是因为应用中存在非守护线程(比如自定义线程池、定时任务线程),JVM会等待所有非守护线程结束才会退出,导致无法正常响应优雅终止信号。

解决方案

1. 使用Spring Boot原生优雅停机(推荐,Spring Boot 2.3+)

Spring Boot 2.3版本开始内置了优雅停机支持,只需添加配置即可自动处理上下文关闭、会话终止、事务收尾:
在application.properties或application.yml中添加:

# 开启优雅停机
server.shutdown=graceful
# 设置停机超时时间,超过后强制终止
spring.lifecycle.timeout-per-shutdown-phase=30s

这个配置会自动监听优雅终止信号,触发Spring上下文的有序关闭,包括调用Bean的@PreDestroy、关闭数据库连接池、终止正在处理的请求等。

2. 注册上下文关闭事件监听器

如果需要自定义停机逻辑(比如关闭特定会话、终止异步任务),可以实现ApplicationListener<ContextClosedEvent>,这个事件会在Spring上下文关闭时触发,比@PreDestroy覆盖范围更广:

@Component
public class CustomShutdownListener implements ApplicationListener<ContextClosedEvent> {

    // 注入需要处理的资源,比如线程池、数据库连接池等
    private final ExecutorService taskExecutor;

    public CustomShutdownListener(ExecutorService taskExecutor) {
        this.taskExecutor = taskExecutor;
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        // 1. 关闭线程池,等待任务完成
        taskExecutor.shutdown();
        try {
            if (!taskExecutor.awaitTermination(20, TimeUnit.SECONDS)) {
                taskExecutor.shutdownNow();
            }
        } catch (InterruptedException e) {
            taskExecutor.shutdownNow();
        }

        // 2. 关闭数据库会话/事务(如果需要手动处理)
        // 比如关闭自定义的数据库连接、终止未提交的事务回滚等

        System.out.println("自定义优雅停机逻辑执行完成");
    }
}

3. 注册JVM ShutdownHook

直接向JVM注册停机钩子,确保在收到优雅终止信号时执行逻辑:

@SpringBootApplication
public class YourApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(YourApplication.class, args);

        // 注册ShutdownHook
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            // 关闭Spring上下文
            context.close();
            // 执行自定义停机逻辑
            System.out.println("通过JVM ShutdownHook执行停机");
        }));
    }
}

4. 解决Windows下taskkill /pid无法关闭的问题

如果应用存在非守护线程,需要将其设置为守护线程,或者在停机时主动关闭:

  • 将自定义线程设置为守护线程:
ExecutorService executor = Executors.newFixedThreadPool(5, runnable -> {
    Thread thread = new Thread(runnable);
    thread.setDaemon(true); // 标记为守护线程,JVM退出时会自动终止
    return thread;
});
  • 或者在停机回调中主动关闭线程池(如步骤2中的代码)。

正确的终止命令使用

  • Linux环境:使用kill -15 <pid>发送优雅终止信号,不要用kill -9
  • Windows环境:使用taskkill /pid <pid>(不带/f),如果还是无法关闭,先检查是否有非守护线程未处理,再考虑是否需要强制终止(万不得已才用taskkill /f)

内容的提问来源于stack exchange,提问作者Kurojin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 12:47:41