重试风暴(Retry Storms)历来是威胁业务连续性与品牌信任的隐形杀手。尽管重试配置调整和重试预算(Retry Budgets)能在服务层面提供一定缓解,但这些手段依赖手动配置,且缺乏对深层依赖链和扇出模式下跨服务放大效应的可见性。这导致系统难以屏蔽由堆栈深处单个服务故障触发的多米诺骨牌效应。

问题的核心在于,传统重试行为缺乏上下文感知能力。工程师可以控制重试次数,却难以精确控制重试时机,根源在于无法可靠区分“服务自身生成的错误”与“仅通过该服务传播的错误”。结果是,重试往往被统一应用而非有条件执行。在瞬态或低速率故障下,这种方法尚且有效;但在中等或严重降级期间,对已挣扎服务的激进重试会增加负载,加速故障,并放大上游依赖项中的重试流量,最终导致全堆栈事件,彻底破坏用户体验。

重试放大的数学逻辑

在一个简单的调用链中,若每个节点都配置为重试一次,当深层节点出错时,请求量会随深度指数级增长。假设每次跳数的重试次数为R,节点深度为d,基础请求量为N,则该节点服务的请求数为 R^d × N。即便引入重试预算(如10%),请求量仍会随深度累积放大。

然而,如果能识别错误源头,限制仅在错误起源节点与其直接调用者之间重试,而禁止更上游节点对此错误进行重试,就能在保证可用性的同时,避免下游节点不堪重负。数据显示,在被调用方可用性下降高达10%的情况下,有限的重试可将调用方感知的可用性提升至99%以上;但超过此阈值,大量请求因触及预算而被丢弃,感知可用性将显著下降。

确立“错误所有权”

Uber提出的解决方案是确立“错误所有权”,其逻辑类似于区分症状与病因。如果服务因下游外部服务出错而返回错误,该错误仅为“症状”;若服务在无下游错误的情况下自行返回错误,则该服务是错误的“原因”及所有者。

基于此,Uber在服务网格中部署了一套上下文感知机制:

  • 声明与取消声明:利用服务依赖分析方案,将传入故障与传出故障关联。若节点检测到下游已声明的内部错误,它在向上游传播错误时会“取消声明”,从而阻止上游节点继续重试。
  • 决策矩阵:通过复杂的决策逻辑,判断是否应重试。例如,当边缘节点处于关闭故障状态且下游返回已声明错误时,直接调用者应重试,但向上游传播时需标记为未声明,以阻断风暴蔓延。
  • 处理偶然错误:针对上下游同时出错的罕见场景(偶然错误),系统利用故障模式记忆,仅在传入故障与关闭故障依赖中的传出故障相关时才取消声明,最大限度减少合法重试机会的丢失。

生产环境实战:拦截950万无效请求

这一机制并非理论构想,而是已完全嵌入Uber共享基础设施的生产级方案。服务无需定制错误处理逻辑,即可自动继承重试风暴保护。

2025年11月18日,Uber遭遇了一次由核心实体服务问题引发的重大停机。该服务位于调用链第5层,因底层基础设施问题返回极高错误率。在传统重试预算下,这将导致降级服务流量增加46%-135%,进一步延长停机时间。但由于启用了错误所有权机制,系统自动限制了爆炸半径:

  • 立即停止了对降级服务直接调用者的重试,部分调用者被阻止发出多达20万个额外请求。
  • 聚合计算显示,系统在根节点处成功拦截了惊人的950万个多余请求,避免了这些请求对已降级服务的持续打击。

结论

通过将重试严格限制在能够真正解决问题的“错误拥有者”服务中,Uber有效防止了重试风暴特有的请求指数级扇出。数据显示,在面向用户的API中,最大重试风暴半径已从25降至最高3,平均值从20降至2。这一架构升级显著减少了降级事件期间的总请求量,保护了基础设施免受单点故障引发的级联崩溃。