本地推理路由的故障切换,我们在一次演练里丢掉的 38 分钟
上次给客户的演示排在下午三点,早上的例行巡检里,我把主后端从 A 池切到了 B 池,想趁没流量试一次真正的故障切换。结果切换脚本在健康检查这一步卡了 38 分钟,差点把演示拖成事故。这篇把当时的排查记录和后来落地的三条规则写下来,都是我们自己踩过的。

本地推理路由的故障切换,我们在一次演练里丢掉的 38 分钟
上次给客户的演示排在下午三点,早上的例行巡检里,我把主后端从 A 池切到了 B 池,想趁没流量试一次真正的故障切换。结果切换脚本在健康检查这一步卡了 38 分钟,差点把演示拖成事故。这篇把当时的排查记录和后来落地的三条规则写下来,都是我们自己踩过的。
第一次切换:健康检查怎么"等"出来的
我们的网关按顺序探活:请求 `/v1/models`,连续 3 次 200 才算健康,超时 5 秒每次。规则本身没问题,问题出在切换脚本的写法——它把"单次探测失败"当成"后端不健康",同时又把"不健康"当成"继续重试",两层语义叠在一起,陷入了 38 分钟的静默循环:日志里每 5 秒一行超时,脚本却认为"还在正常等待"。
最后发现 B 池的镜像没同步完成,`/v1/models` 返回的是 503 而不是超时报错。健康检查只看"有没有 200",于是把所有非 200 都归入"再等等"。
**规则一:明确区分"探活失败"和"后端确认不健康",并给总等待时间设上限。**现在我们切换的熔断条件是两个:单后端连续 5 次探测失败,或者总等待超过 5 分钟,二者先到者触发回切。切换脚本里这两个门槛都写成了命令行参数,不留"默认一直等"的位置。
第二次切换:重试预算被吃光了
一周后真实故障来了,A 池里一台节点的推理进程挂了。网关自动切到 B 池这次很干净,但客户侧反馈首个请求延迟飙到了 20 秒。查下来不是网络问题,是重试放大:客户端 SDK 默认 3 次重试,网关层 2 次重试,每次重试都重新排队,请求实际被排队了 6 次以上。
**规则二:重试预算必须在链路上做全局限额,而不是每层各自设。**我们把客户端重试改成"整个请求生命周期最多 2 次",网关重试只对幂等的健康探测开,对实际推理请求一律不重试,超时直接回切换池。改完之后同一场景的首请求延迟稳定在 4 秒以内。这里有个容易忽略的点:LLM 推理请求看起来幂等(同样的 prompt 重复提交结果一样),但在排队系统里"重复提交"会重复占队列,效果上等价于非幂等。
第三个坑:时钟漂移让"切换时间"没法对齐
复盘时要把两边日志拼起来,发现 A 池和 B 池的时钟差了 40 秒,是机器重启后 NTP 没跟上的锅。40 秒的差值让"哪个请求在哪一侧失败"这种问题查起来非常费劲。
**规则三:时钟同步和健康检查一样,都是基础设施假设,要当成服务自己探测的一环。**现在网关每 10 分钟校准一次本地时钟偏差,偏差超过 5 秒直接报警;切换日志里所有时间戳强制带时区标记,禁止使用未标记的本地墙钟时间。
三条规则进 CI 之后
这三个月里这套演练每月跑一次:切主、断网、模拟镜像半同步。总共回滚过一次(镜像仓库带宽不够导致 B 池起不来),其余都按预期走完。触发过的告警都留了记录,没有一次是"静默失败"。
本地推理的路由层其实就干三件事:探活、排队、切换。把这三件事各自的失败模式写清楚,切换演练就不会只是"点了一下按钮"。比起换更高级的调度框架,先把"失败后的行为"定义清楚,性价比更高。
留言区
欢迎分享你的想法!
加载留言中…