Java 服务优雅关闭

进程收到终止信号后如何排空请求、关闭线程池与释放连接。

#type / concept #status / growing #tech / dev #resource / java

[!info] 关联笔记

Java 服务优雅关闭

这个概念为什么出现

容器编排滚动发布会发 SIGTERM。若进程直接死,请求半截、连接泄漏、任务丢失。优雅关闭定义停止接流 → 排空 → 释放的顺序。

[!abstract] 一句话理解 捕获终止信号/钩子,停止接收新请求,等待在途完成(有超时),再关闭线程池与外部连接。

最小可运行示例

场景:注册关闭钩子停止线程池

import java.util.concurrent.*;

public class GracefulDemo {
    public static void main(String[] args) throws Exception {
        ExecutorService pool = Executors.newFixedThreadPool(2);
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("shutdown-begin");
            pool.shutdown();
            try {
                if (!pool.awaitTermination(2, TimeUnit.SECONDS)) {
                    pool.shutdownNow();
                }
            } catch (InterruptedException e) {
                pool.shutdownNow();
            }
            System.out.println("shutdown-end");
        }));
        pool.execute(() -> System.out.println("work"));
        Thread.sleep(200);
        System.exit(0); // 触发钩子(演示)
    }
}
javac GracefulDemo.java && java GracefulDemo

期望输出包含 work 与 shutdown-begin/end(顺序可围绕 exit 语义)。

结合场景再看三个关注点

  1. 钩子要快,超时强杀。
  2. HTTP 服务器 stop 延迟参数。
  3. 与 K8s preStop/terminationGracePeriod 对齐。

核心概念与准确模型

1. 信号与钩子

2. 排空策略

3. 数据一致性:在途事务

边界情况与反直觉行为

  1. 钩子中死锁。
  2. 日志框架先关闭导致钩子日志丢失。
  3. kill -9 无法优雅。

常见误区

[!warning] 常见误区:只关 HTTP 不关消费者线程 消息消费/定时任务同样要停。

工程实践

  1. readiness 先 fail,再停服务。
  2. 超时可配置。
  3. 指标记录关闭耗时。
  4. 演练强制杀死与优雅路径。

本节总结

  • 停新→排空→释放
  • 超时兜底
  • 与编排对齐

自测题

  1. 为什么 readiness 要先变假?
  2. shutdown 与 shutdownNow?
参考答案
  1. 让负载均衡停止派发新流量。
  2. 温和停止 vs 尝试中断在途。

延伸阅读与资料来源

资料类型支撑内容
Runtime.addShutdownHookAPI钩子
创建于 2026/7/15 更新于 2026/7/15