一、什么是优雅停机
想象你正在看一部电影,突然有人直接拔掉电源,画面瞬间消失——这就是暴力停机的后果。而优雅停机更像是按下遥控器的暂停键,让系统有机会保存进度、关闭连接,最后安全退出。
在Spring Boot应用中,优雅停机指的是:当需要关闭服务时,先拒绝新请求,处理完存量任务,释放数据库连接等资源,最后终止进程。这样能避免数据丢失或事务中断,就像餐厅打烊前会让现有顾客吃完,但不再接待新客人。
二、为什么需要优雅停机
线上服务重启或更新时,直接kill -9可能导致:
- 数据库事务中途断开,留下"半成品"数据
- 消息队列消费到一半,消息既没成功也没回滚
- 用户突然收到错误提示,体验断崖式下跌
典型场景举例:
- 支付系统正在处理转账,突然停机可能导致A账户已扣款,B账户未到账
- 电商订单提交到一半,库存已经扣除但订单记录丢失
三、Spring Boot实现方案详解
技术栈:Spring Boot 2.7 + Tomcat + JDBC
方案1:使用Actuator端点(适合简单应用)
// 启用shutdown端点(application.yml)
management:
endpoint:
shutdown:
enabled: true
endpoints:
web:
exposure:
include: shutdown
// 调用方式:POST /actuator/shutdown
// 注意:需要安全认证,建议配合Spring Security使用
缺点:无法自定义停机逻辑,比如等待任务队列清空。
方案2:自定义停机钩子(推荐)
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApp.class);
// 注册停机钩子
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("收到停机信号,开始清理...");
// 示例:关闭数据源连接池
HikariDataSource dataSource = app.run(args).getBean(HikariDataSource.class);
dataSource.close(); // 优雅关闭连接池
// 这里可以添加其他清理逻辑,比如:
// 1. 停止接收新MQ消息
// 2. 等待正在处理的线程完成
// 3. 持久化缓存数据
}));
app.run(args);
}
}
方案3:结合Spring事件机制(更精细控制)
@Component
public class GracefulShutdown {
@Autowired
private DataSource dataSource;
@EventListener(ContextClosedEvent.class) // Spring容器关闭事件
public void onShutdown() throws InterruptedException {
// 第一步:标记服务不可用
HealthIndicator healthIndicator = () -> Health.down().build();
// 第二步:等待30秒让现有请求完成
Thread.sleep(30000);
// 第三步:关闭数据库连接池
if(dataSource instanceof HikariDataSource) {
((HikariDataSource)dataSource).close();
}
}
}
四、关键注意事项
超时时间设置:
等待处理中的请求时,必须设置合理超时(如30秒),避免无限等待。可以在配置中添加:server: shutdown: graceful spring: lifecycle: timeout-per-shutdown-phase: 30s分布式系统协调:
如果是微服务架构,还需要:- 从注册中心(如Nacos)主动下线服务
- 通过API网关切断流量
线程池处理:
自定义线程池需要额外关闭逻辑:@Bean(destroyMethod = "shutdown") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setAwaitTerminationSeconds(60); // 等待任务完成 executor.setWaitForTasksToCompleteOnShutdown(true); return executor; }测试验证方法:
- 发送请求后立即执行
kill -15 [PID](SIGTERM信号) - 观察日志是否显示清理流程
- 检查数据库事务是否完整提交
- 发送请求后立即执行
五、不同场景下的选择建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 单体应用简单需求 | Actuator端点 | 无需编码,快速实现 |
| 需要自定义清理逻辑 | 停机钩子+Spring事件 | 灵活控制各模块关闭顺序 |
| 使用Kubernetes | 配置preStop Hook | 与容器编排系统深度集成 |
六、总结
优雅停机就像"安全着陆",核心思路是:
- 信号通知:通过SIGTERM或API触发流程
- 流量切断:拒绝新请求但处理存量
- 资源清理:按顺序关闭数据库、MQ等连接
- 最终退出:确认无误后终止进程
完整的实现示例可在GitHub找到(模拟项目地址)。实际应用中,建议结合Prometheus监控确保每次停机都符合预期耗时。
评论