Welcome
admin
admin

2026-07-01 20:29:41

葡萄牙世界杯球员
2247 344

40.线上服务如何优雅的停机?前言最近星球中有位小伙伴问了我一个问题:如何优雅的停机?

我觉得这个问题挺有代表性的。

今天这篇文章跟大家一下优雅停机的一些常见方案,希望对你会有所帮助。

点击这里👇🏻获取:企业智能知识库系统、100万QPS短链系统、复杂的商城微服务系统、智能翻译助手AI Agent、SaaS点餐系统、刷题吧小程序、商城系统、秒杀系统、AI项目、代码生成神器、企业智能招聘系统、智能天气播报AI Agent、智能代码审查AI Agent、智能商品推荐系统等 12 个项目的:项目源代码、开发教程和技术答疑

1.什么是优雅停机?优雅停机(Graceful Shutdown) 指在服务终止前,系统能:

拒绝新请求进入完成存量请求处理释放所有资源通知上下游服务非优雅停机的惨痛代价:

真实案例:支付回调丢失。

// 支付回调处理

@PostMapping("/callback")

public void handleCallback(Payment payment) {

// 1. 更新订单状态

orderService.updateStatus(payment.getOrderId(), PAID);

// 2. 发放权益(kill发生时此处未执行)

benefitService.grantVip(payment.getUserId());

}当kill发生在步骤1和2之间时,导致订单状态已更新但权益未发放,引发用户投诉。

2.优雅停机三大核心流程2.1 信号捕获层

2.2 流量控制层

2.3 资源释放层

3.Spring Boot优雅停机的实现3.1 基础配置在SpringBoot项目的application.yml文件中增加如下配置:

server:

shutdown: graceful # 开启优雅停机

spring:

lifecycle:

timeout-per-shutdown-phase: 30s # 最长等待时间3.2 线程池优雅关闭在线程池中实现优雅关闭功能:

@Bean

public ExecutorService threadPool() {

ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

executor.setWaitForTasksToCompleteOnShutdown(true); // 等待任务完成

executor.setAwaitTerminationSeconds(60); // 最大等待时间

return executor.getThreadPoolExecutor();

}在shutdown之前,先等待任务完成。

3.3 分布式锁释放拦截器@Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")

public Object handleRequest(ProceedingJoinPoint pjp) {

Lock lock = redisson.getLock("order_lock");

try {

lock.lock();

return pjp.proceed();

} finally {

if (!isShuttingDown()) {

lock.unlock(); // 非停机时正常释放

}

// 停机时由锁管理器统一释放

}

}使用统一的拦截器释放分布式锁,防止出现异常有释放遗漏的地方。

4.Kubernetes环境下的优雅停机最近建一些几十个工作内推群,各大城市都有,群里目前已经收集了很多内推岗位,大厂、中厂、小厂、外包都有。 欢迎HR、开发、测试、运维和产品加入。

扫描下方微信,备注:网站+所在城市,即可拉你进工作内推群。

4.1 关键配置STOPSIGNAL SIGTERM # 使用SIGTERM替代SIGKILL# Deployment配置

spec:

terminationGracePeriodSeconds: 60 # 宽限期

containers:

- lifecycle:

preStop:

exec:

command: ["/bin/sh", "-c", "sleep 20;"] # 预留缓冲时间在部署配置中增加预留缓冲时间。

4.2 就绪探针自动摘流

5.中间件连接优雅关闭5.1 数据库连接池@PreDestroy

public void close() {

HikariPool pool = dataSource.getHikariPoolMXBean();

pool.suspendPool(); // 停止借出连接

pool.softEvictConnections(); // 驱逐空闲连接

while (pool.getActiveConnections() > 0) {

Thread.sleep(500); // 等待活动连接完成

}

pool.shutdown(); // 彻底关闭

}使用@PreDestroy在服务销毁之前关闭数据库连接池。

5.2 RabbitMQ消费者@PreDestroy

public void stop() {

channel.basicCancel(consumerTag); // 取消订阅

while (unackedMessages.get() > 0) {

Thread.sleep(100); // 等待ACK完成

}

connection.close();

}@PreDestroy在服务销毁之前取消订阅,需要先等待ACK完成。

3. Redis分布式锁public class LockManager implements DisposableBean {

@Override

public void destroy() {

lockMap.forEach((key, lock) -> {

if (lock.isHeldByCurrentThread()) {

lock.unlock(); // 强制释放未解锁的锁

}

});

}

}实现DisposableBean接口,在服务销毁之前强制释放未解锁的锁。

6.全链路优雅停机6.1 停机事件传播机制

6.2 状态机管理public enum ShutdownState {

RUNNING, // 正常运行

PRE_SHUTDOWN, // 拒绝新请求

DRAINING, // 排空存量请求

TERMINATED // 完全终止

}6.3 停机监控面板

7.生产环境避坑指南7.1 必须避免的四大陷阱陷阱后果解决方案死锁等待无法完成停机设置锁超时时间第三方服务不可用资源无法释放添加熔断机制长周期任务超过宽限期被强杀拆分任务+保存中间状态文件写入未完成数据损坏使用原子文件替换7.2 停机检查清单# 停机前执行

curl -X POST http://localhost:8080/actuator/shutdown-prepare

# 验证项:

1. 新请求返回503

2. 活动线程数持续下降

3. 数据库连接数归零

4. MQ无未ACK消息7.3 黄金法则:二段式停机

总结基础层:处理HTTP请求 Spring Boot Graceful Shutdown + 线程池等待

进阶层:管理中间件连接 数据库连接池排空 + MQ消费者取消订阅

高级层:分布式协同 停机事件广播 + 分布式锁释放

终极层:全链路状态管理 停机状态机 + 智能超时控制

停机策略对比表

策略实现难度停机时间数据安全适用场景直接kill -9☆秒级极低开发环境Spring Boot☆☆10-30s中常规Web应用容器化方案☆☆☆可配置高K8S环境全链路管理☆☆☆☆分钟级极高金融核心系统