资讯动态

SpringBoot优雅停机,别再kill-9了

发布时间:2026/9/15 3:49:12 来源:尧图企业网站定制
线上发布时你有没有用过kill -9强行终止应用进程瞬间消失部署脚本跑得飞快看起来一切正常。但用户那边可能正在提交订单、正在支付回调、正在上传文件——这些请求在毫秒之间被腰斩数据写了一半消息发了一半分布式锁没释放数据库连接没归还。一次kill -9的代价可能是一批用户投诉、一笔对账差账、甚至一次线上故障。从 Spring Boot 2.3 开始框架正式内置了优雅停机功能覆盖 Tomcat、Jetty、Undertow 和 Reactor Netty 四种内嵌 Web 服务器。但直到今天很多团队依然没有开启它。开启优雅停机只需要两行配置在application.yml中加上两个配置项即可text复制下载server: shutdown: graceful spring: lifecycle: timeout-per-shutdown-phase: 20s第一行告诉 Spring Boot 收到关闭信号后不要立即终止而是先停止接收新请求再等待正在处理的请求完成。第二行设置宽限期默认值是 30 秒超过这个时间仍未完成的请求会被强制中断。不同 Web 服务器的行为略有差异Tomcat、Jetty 和 Reactor Netty 在网络层直接拒绝新连接Undertow 则会接受新请求但立即返回 503因为它的线程模型决定了“不接收”不如“快速拒绝”更安全。Kubernetes 环境下仅靠 Spring Boot 配置还不够如果你在 K8s 中部署有一个关键陷阱Spring Boot 的优雅停机处理的是“已经到达 Pod 的请求”但它无法阻止 Kubernetes 在 Pod 被摘除之前继续向它转发新流量。K8s 默认在发送 SIGTERM 信号的同时就开始了从 Service Endpoint 中移除 Pod 的操作——但负载均衡器的状态同步存在延迟。正确的做法是在 Deployment 中配置preStopHook在 SIGTERM 发出前预留一段缓冲时间text复制下载lifecycle: preStop: exec: command: [/bin/sh, -c, sleep 15]这 15 秒的作用是让 K8s 有足够时间将这个 Pod 从 Endpoint 中摘除确保所有新流量已经停止转发到该实例。之后再发送 SIGTERMSpring Boot 才开始优雅停机流程。同时terminationGracePeriodSeconds必须大于preStop的 sleep 时间加上 Spring Boot 的宽限期否则容器还没完成清理就会被 K8s 强制杀死。别忘了清理你自定义的资源Spring Boot 的优雅停机默认只负责 Web 层的请求排空。如果你在代码中自定义了线程池、调度器或消息队列监听器这些资源需要你自己在PreDestroy方法中关闭。线程池的优雅关闭关键在两个参数setWaitForTasksToCompleteOnShutdown(true)让线程池在关闭前等待队列中的任务执行完毕setAwaitTerminationSeconds(60)设置最大等待时间。对于数据库连接池可以在PreDestroy中先暂停连接借出再等待活跃连接归还。有一个容易被忽略的坑如果你的线程池是通过new关键字自己创建的而不是交给 Spring 容器管理PreDestroy不会被调用必须手动注册销毁逻辑。从“启动时优雅”到“关闭时优雅”Spring Boot 优雅停机的意义不在于它用了多复杂的技术——底层不过是 JVM 的 Shutdown Hook 机制。它的真正价值在于提醒我们一个服务的生命周期不应该只有“启动”和“运行”两个状态。关闭同样是一个需要认真设计的阶段它决定了你的系统在滚动更新、弹性扩缩容、故障迁移时是让用户无感还是让用户买单。下次发布时把kill -9换成kill -15把那两行配置加上。这不是一个需要纠结的技术选型而是一个应该成为默认习惯的工程素养。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价