关键在于借助kubernetes或nginx协调新旧实例流量切换,配置readinessprobe指向/actuator/health/readiness、prestop钩子与优雅停机,确保新实例就绪后导流、旧实例无请求后再终止。

在 Spring Boot 中实现平滑滚动升级与零停机部署,关键不是靠单体应用“自己重启”,而是借助外部编排系统(如 Kubernetes)或反向代理(如 Nginx)协调新旧实例的流量切换,同时确保 Spring Boot 本身具备状态可控、响应可预期的能力。核心在于:**让新实例真正就绪后再导流,让旧实例彻底无请求后再终止。**
用 Kubernetes 实现滚动更新 + 探针控制
K8s 是生产环境最主流的落地方式。它本身不理解 Java 应用启动耗时,必须靠探针显式告诉集群“什么时候能收流量”“什么时候可以杀”。
-
必须配置 readinessProbe:指向 Spring Boot Actuator 的
/actuator/health/readiness端点(Spring Boot 2.3+ 需启用该健康组),不能只用默认的/actuator/health。否则容器一启动就被加到负载均衡池,而此时 Tomcat 可能刚初始化、数据库连接还没建好。 - initialDelaySeconds 要留足余量:比如设为 30 秒,覆盖典型 Spring Boot 启动耗时(类加载、Bean 初始化、缓存预热等),避免探针过早失败导致反复重启。
-
配合 preStop 生命周期钩子:在 Pod 终止前执行
sleep 10或调用自定义 shutdown 接口,给正在处理的请求留出完成时间;同时配合terminationGracePeriodSeconds(建议 ≥30 秒),防止 SIGTERM 发出后立即被 SIGKILL 强杀。 -
Deployment 策略要合理:设置
maxSurge: 1(允许多起一个新 Pod)、maxUnavailable: 0(更新中不允许不可用实例),确保始终有副本在线。
用 Nginx + 多端口实现简易滚动发布
适用于未上 K8s 的轻量级生产环境(如宝塔面板、传统服务器),通过 Nginx upstream 动态摘除/添加后端节点。
- 新版本启动时指定临时端口(如
--server.port=9090),老版本继续运行在 8080; - 新服务启动成功后,用脚本修改 Nginx upstream 配置,把 9090 加入、8080 暂时注释或权重设为 0;
- 执行
nginx -s reload(毫秒级生效,不中断连接); - 观察日志确认无新请求打到 8080 后,再手动 kill 老进程。
Spring Boot 自身必须支持优雅停机
这是零停机的前提条件——否则即使外部调度得当,旧实例也可能在收到终止信号瞬间丢弃请求。
- Spring Boot 2.3+ 直接配置:
server.shutdown=graceful; - 设置关闭超时:
spring.lifecycle.timeout-per-shutdown-phase=60s,确保长事务、异步任务有足够时间收尾; - 检查是否有线程池未正确 shutdown(如自定义 @Bean ThreadPoolTaskExecutor),需显式调用
shutdown()并 await; - 若使用 Netty(WebFlux),同样需确认 Reactor 线程组能响应关闭信号。
避免踩坑的关键细节
很多 502/404 错误不是配置没写,而是细节没对齐:
- Actuator 健康端点路径要和 readinessProbe 完全一致,且确保该 endpoint 在生产 profile 下未被禁用;
- readinessProbe 的
failureThreshold × periodSeconds必须大于应用冷启动最大耗时,否则会触发连续失败→Pod 被反复重建; - 不要依赖 livenessProbe 来“探测是否启动完成”,它只管“是否活着”,应由 readinessProbe 承担就绪判断;
- 如果用了注册中心(如 Nacos/Eureka),确保服务下线逻辑与 readiness 探针联动,避免注册中心已剔除但 kube-proxy 还在转发。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











