重启是最频繁的「故障」
发版重启是运维中最频繁的操作——每一次重启,如果不做优雅停机,都在制造一次小型故障:正在处理的请求被掐断(用户看到 502)、注册中心的实例状态混乱(流量打到已死实例)、MQ 的消费位点没提交(消息重复消费)、缓存的本地数据丢失(重启后缓存雪崩)。「重启不发事故」是后端服务的基本功——这篇把优雅停机的每个环节拆开。
停机的信号与流程
停机的正确姿势:SIGTERM 的处理(kill 默认发 SIGTERM——「优雅停机的入口」:应用注册 SIGTERM 的 handler——「handler 里做什么」:进入停机流程(不再接新请求、处理完存量请求、清理资源——「SIGKILL 的区别」(kill -9 发 SIGKILL——「无法捕获」——进程直接终止、「一切优雅都不存在」——「SIGKILL 是最后手段」(优雅停机超时后的强制手段——「超时的设置」(优雅停机的时间预算(30-60 秒——「超时的兜底」。停机流程的完整序列:「序列的七步」:第一步:摘除流量(从注册中心下线或网关摘除——「摘除的时机」:越早越好(「先摘流量」:新请求不再进来——「注册中心的摘除延迟」(注册中心的缓存与推送延迟(Nacos/Eureka 的推送——「摘除不等于生效」——「生效的验证」:网关与调用方的缓存刷新时间——「摘除后的等待」(等调用方的服务列表刷新(「等待窗口」(几秒——「摘除-等待-停机」的节奏。第二步:停止接收新请求(HTTP 服务器关闭监听——「监听的关闭」(端口还在但拒绝新连接——「已有连接的处理」(保持已有连接处理完。第三步:处理存量请求(等待进行中的请求完成——「存量请求的等待」(超时的强制断开——「长请求」的处理(超时时间与优雅停机窗口的匹配——「超长请求」的兜底。第四步:停止接收新消息(MQ 消费者的暂停——「MQ 的停机」(消费者的关闭——「消费中的消息」(正在处理的消息完成——「位点的提交」(处理完再提交——「位点提交」与「重复消费」(停机时的位点提交失败=重启后重复消费——「幂等」是重复消费的保险(幂等设计篇。第五步:清理定时任务(进行中的定时任务的处理(「定时任务的停机」——正在跑的任务等完还是中断——「任务的中断点」(任务要支持断点(可重跑——「任务的可重跑」设计。第六步:刷写与提交(本地缓存的刷写、统计的提交——「本地状态的持久化」(内存中的统计数据刷盘——「状态的外置」(重要状态放 Redis 而非本地内存——「状态外置」避免停机损失。第七步:关闭资源(连接池、线程池的关闭——「资源的关闭顺序」(先业务后基础——「关闭的日志」(停机过程的完整日志——「停机日志」是排查的依据。
graph TD
A[优雅停机七步] --> B[1 摘流量: 注册中心下线+等待生效]
A --> C[2 停新请求: 关闭监听]
A --> D[3 处理存量: 等待完成+超时兜底]
A --> E[4 停新消息: MQ 消费暂停+位点提交]
A --> F[5 清理任务: 定时任务可重跑]
A --> G[6 刷写提交: 状态持久化]
A --> H[7 关闭资源: 先业务后基础]
发布体系的配合:停机不是孤立的动作
发布的滚动策略:「滚动发布」与优雅停机的配合(蓝绿金丝雀篇的发布策略——「滚动发布的节奏」(一台一台重启——「滚动期间的服务能力」(停机窗口的服务能力下降——「降级窗口」(发布期间预降级——「发布期间的容量」:余量要覆盖「正在重启的机器」——「容量的发布折损」——「并发的发布」(多台同时重启=能力骤降——「串行发布」的时长——「发布速度与稳定性的权衡」。「K8s 的优雅停机」:「K8s 的停机机制」:Pod 的 terminationGracePeriodSeconds(「grace period」的设置(默认 30 秒——「preStop hook」(终止前的钩子(「preStop」的用途(摘除流量的动作(sleep 等待生效——「preStop sleep」的技巧(sleep 5-10 秒等流量摘除生效——「terminationGracePeriod」与「preStop」的配合(preStop 时间 < grace period——「K8s 的 kill 流程」(SIGTERM→grace period→SIGKILL——「应用的信号处理」在 K8s 的必须性。「健康检查」的配合:「readiness probe」的角色(就绪探针失败=摘除流量——「readiness 的先于 SIGTERM」——探针失败后流量摘除——与 SIGTERM 的停机流程配合——「readiness」是优雅停机的第一道闸。「发布期间的监控」:「发布监控」的指标(错误率、延迟的发布期间观察——「发布的自动回滚」(错误率超阈值的自动回滚——「自动回滚」的触发条件——「发布即变更」的监控纪律。
优雅停机的进阶:K8s 场景的完整实践与「发布的熔断」
优雅停机的进阶:K8s 场景的完整实践与发布期间的熔断保护。「K8s 优雅停机」的完整链路:「Pod 终止的完整流程」:EndpointSlice 摘除(「Endpoint 的摘除」(kube-proxy 的规则更新——「摘除的传播」(不同代理模式的传播延迟(iptables vs IPVS——「IPVS vs iptables」的延迟差异——「摘除延迟」的实测——preStop 的 sleep(「sleep 的时长」按摘除延迟调——「 readinessGates」的高级控制——「preStop + readiness」的配合(readiness 先失败、preStop 再 sleep——「双保险」的摘除——「SIGTERM 的 handler」(应用内的信号处理——「应用的 shutdown hook」(Spring 的 graceful shutdown——「各语言的支持」(Java/Go/Node 的优雅停机支持。「发布期间的熔断保护」:「发布窗口」的服务能力下降:「能力折损」的计算(重启中的实例不处理请求——「折损的余量」(发布期间的余量设计——「发布速率」的控制(串行发布的速率与折损——「发布期间的降级」(预降级非核心功能——「发布窗口的容量」要覆盖折损。「发布的「熔断」:「发布的错误率熔断」:发布引发的错误率上升——「熔断的阈值」(错误率的发布熔断阈值——「自动回滚」(熔断触发自动回滚——「回滚的速度」(回滚的时间目标——「回滚的演练」(回滚也要演练——「回滚预案」的完备性。「发布的「灰度验证」:「灰度的实例验证」:新版本的实例先灰度(「灰度实例」的流量比例——「灰度的指标对比」(新版本 vs 旧版本的指标对比——「指标对比」的维度(错误率、延迟、业务指标——「灰度的推进」(指标正常才推进——「灰度的决策」(推进/回滚的决策依据——「灰度验证」是发布的质量闸门。优雅停机的进阶总结:K8s 的完整链路(EndpointSlice 摘除延迟、preStop sleep、readiness+preStop 双保险、应用的 shutdown hook)+发布的熔断保护(能力折损、错误率熔断、自动回滚、回滚演练)+灰度验证(实例灰度、指标对比、灰度决策)——「优雅停机」在 K8s 场景是一套「多组件配合」的体系——摘除延迟的感知、preStop 的等待、应用的 hook、探针的配合——「每一个组件的时序」都要对齐——发布期间的熔断与灰度,则是把「发布」从「风险操作」变成「受控变更」的关键——「变更的可控性」是生产环境的底线能力**。
优雅停机高频问答
收尾前回答三个高频问题。「kill -9 的时候全都没用了怎么办?」——SIGKILL 的场景:「SIGKILL 的来源」:K8s 的 grace period 超时——「grace period 的调大」(terminationGracePeriodSeconds——「超时的原因」(存量请求处理慢——「慢请求」的治理(超时的设置——「SIGKILL 的兜底逻辑」(应用层的崩溃恢复——「重启后的恢复」(消息重复、状态丢失——「幂等与状态外置」的保险——「SIGKILL 是常态化的假设」——「应用假设随时可能被杀」——「crash-safe」的设计(随时挂都能恢复。「发布时能不能不停机?」——不能完全避免但可最小化:「零停机」的极限:蓝绿发布的切换(「蓝绿」的切换瞬间(切换的原子性——「蓝绿的资源」(双倍的资源——「蓝绿 vs 滚动」的选择——「滚动」的折损窗口——「蓝绿」的资源成本——「折损的最小化」(优雅停机+滚动串行——「零停机」的定义(用户无感——「用户无感」的标准(错误率不升、延迟不涨——「无感」是目标不是承诺。优雅停机的问答收束:优雅停机的三个心智模型——「摘流量是关门(不再接客)、处理存量是送客(送走现有客人)、SIGKILL 是拆迁(突然断电)」——「关门→送客→才拆迁」的顺序——「拆迁随时可能来」——所以「状态外置+幂等」是拆迁保险——「优雅停机的练习」:每次发版都按流程走——**「流程的肌肉记忆」靠每次发版的执行。
停机的「验证清单」
优雅停机的验证清单:「上线前的自检五问」:一问 SIGTERM handler 注册了吗(kill 测试过);二问 摘流量后有等待窗口吗(注册中心缓存);三问 存量请求有超时兜底吗(不会无限等);四问 MQ 位点提交了吗(重启不重复消费);五问 K8s 的 preStop 配了吗(grace period 匹配)。五问全过,重启才算优雅。「验证的方法」:手动 kill 测试观察日志、发版观察错误率曲线——**「错误率的平滑」是优雅停机的成绩单。
小结
优雅停机的完整体系:停机序列的七步(摘流量(含等待生效窗口)→停新请求→处理存量→停消息(位点提交)→清理任务(可重跑)→刷写状态→关闭资源——SIGTERM 的 handler 是入口、超时的 SIGKILL 是兜底);发布体系的配合(滚动发布的节奏与容量折损、K8s 的 preStop hook 与 readiness probe 的配合、发布监控与自动回滚);幂等的保险(位点提交失败=重复消费——幂等是所有「停机缺陷」的最后保险)。优雅停机的本质:把「重启」从一个故障事件变成一个受控过程——每一个细节(摘流量、等生效、位点提交)都是「从故障到受控」的转化点——检查你的应用:SIGTERM 的 handler 注册了吗、存量请求处理了吗、MQ 位点提交了吗、K8s 的 preStop 配了吗——四个「是」,就是「重启不发事故」的全部门槛。
优雅停机的价值不在平时(平时它隐形),在每一次发版的深夜——那晚你的用户没有看到 502,就是你所有细节的回报。
读者留言
COMMENTS 暂无还没有留言,来说第一句?