← [ BACK_TO_NOTES ]

Java 服务优雅关闭

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

#concept #growing #dev #java

[!info] related notes

Java 服务优雅关闭

这个概念为什么出现

容器编排滚动发布会发 SIGTERM。若进程直接退出,在途请求会中断、未提交任务可能丢失;OS 会回收进程资源,但不替应用完成事务、确认消息或刷新缓冲。优雅关闭定义停止接流 → 排空 → 释放的顺序。

[!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();
                    if (!pool.awaitTermination(1, TimeUnit.SECONDS)) {
                        System.err.println("workers-not-terminated");
                    }
                }
            } catch (InterruptedException e) {
                pool.shutdownNow();
                Thread.currentThread().interrupt();
            }
            System.out.println("shutdown-end terminated=" + pool.isTerminated());
        }));
        pool.execute(() -> System.out.println("work"));
        System.exit(0); // 触发钩子(演示)
    }
}
javac GracefulDemo.java && java GracefulDemo

正常运行输出包含 work、shutdown-begin 和 shutdown-end terminated=true。work 与 shutdown-begin 的先后取决于调度;关闭会等待已提交任务,本例无需用 sleep 猜测其完成时间。

结合场景再看三个关注点

  1. 钩子必须有时间预算;shutdownNow 只是尝试中断,不会强杀 Java 线程。进程最终强制终止由运行环境负责,不能把钩子返回当作任务已成功完成。
  2. HTTP 服务器 stop 延迟参数。
  3. 与 K8s preStop/terminationGracePeriod 对齐。

核心概念与准确模型

1. 信号与钩子

正常 JVM 关闭(包括 System.exit,以及通常配置下的终止信号)会启动已注册钩子。多个钩子并发执行、顺序未指定;不要依赖其他钩子先完成。Runtime.halt、SIGKILL、崩溃等可能完全跳过钩子。

2. 排空策略

先停止接入请求、消费消息和调度新任务,再等待在途任务并按依赖顺序释放连接。readiness 变化传播需要时间,也不会立即切断已有连接;还需服务本身停止接收新工作。Kubernetes 的 preStop 时间计入终止宽限期,应把整个流程放进同一个时间预算。

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

停机无法保证所有在途工作完成;应结合事务、可重试/幂等操作与消息确认时机。不要在结果尚未确定时仅因进入关闭流程就确认任务成功。

边界情况与反直觉行为

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

常见误区

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

工程实践

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

本节总结

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

自测题

  1. 为什么 readiness 要先变假?
  2. shutdown 与 shutdownNow?
参考答案
  1. 通知就绪性检查的使用方停止路由新流量;这有传播延迟,不能代替服务端停止接入。
  2. shutdown 拒绝新任务但执行已提交任务;shutdownNow 尝试中断运行任务并返回尚未开始的任务列表,二者都不等待终止,需另用 awaitTermination。

延伸阅读与资料来源

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

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本