Exponential Backoff
解释重试间隔为什么应随失败次数增长,并通过 jitter 避免大量客户端同步重试形成重试风暴。
#concept
#growing
#backend
#reliability
[!info] related notes
- 所属 MOC:Reliability Engineering MOC
- 上层策略:Retry Policy
- 相邻:Circuit Breaker、接口幂等设计
Exponential Backoff
[!abstract] 一句话定义 Exponential Backoff 让连续重试之间的等待时间按指数增长,并加入随机抖动(Jitter),给故障依赖恢复空间,避免大量客户端在同一时刻重新冲击系统。
基本模型
base delay = 200 ms
首次调用失败后的第 1 次重试(retry_index 0)→ 200 ms
第 2 次重试(retry_index 1)→ 400 ms
第 3 次重试(retry_index 2)→ 800 ms
第 4 次重试(retry_index 3)→ 1600 ms
通常还要设置最大 Delay 和总 Retry Budget:
exponential_delay = min(cap, base × 2^retry_index)
// 上述为未加 jitter 的等待;首次调用不需要先退避
Backoff 只决定“等多久”,不决定“该不该重试”。
为什么立即重试会更糟
service overloads
→ clients fail
→ all retry immediately
→ load increases exactly during failure
→ service remains overloaded
这叫 Retry Storm。固定间隔同样可能让客户端保持同步脉冲。
为什么需要 Jitter
如果一万个客户端都按 1s、2s、4s 重试,它们仍然会在相同时间聚集。
常见思路:
- Full Jitter:在
[0, exponential_delay]随机; - Equal Jitter:保留一部分固定等待,再随机剩余;
- Decorrelated Jitter:下一次延迟根据上次延迟随机变化。
没有一种对所有负载都绝对最好,目标是打散同步并控制尾延迟。
尊重服务端反馈
HTTP Retry-After 可使用秒数或 HTTP 日期,需按契约解析。有效的服务端等待下限不能被本地 cap 或向下抖动缩短;可在下限之上加抖动。若剩余预算不足以等待并再次执行,就结束本次重试,而不是提前请求。
server says retry after 10s
client total deadline only 5s
→ do not retry; return or fallback
哪些错误不应盲目 Backoff 重试
- 400 Schema / Parameter Error;
- 401 Credential Error;
- 403 Permission Denied;
- 404 确定不存在;
- Domain Conflict;
- Prompt / Tool 语义错误;
- 已知不可幂等且结果未知的副作用。
先分类,再 Backoff。
Retry Budget
同时限制:
- 最大 Attempt;
- 总耗时;
- 单次 Timeout;
- 全局额外流量比例;
- 每个 Provider / Tenant 的重试配额。
否则单个请求可能受控,但全系统的 Retry Traffic 仍占满容量。
最小伪代码
max_attempts 包含首次调用,且至少为 1
for attempt in 0 .. max_attempts - 1:
if cancelled or deadline exhausted: stop
result = call(remaining_budget)
if success: return result
if error is not retryable: fail
if side effect is not safe to repeat: reconcile or fail
if attempt == max_attempts - 1: break
delay = compute_backoff(attempt, cap, valid_retry_after)
if delay leaves no execution budget: break
cancellable_wait(delay)
fail or permitted fallback
设计检查表
- Error Classification 在计算 Delay 之前。
- 有最大 Delay 和总 Deadline。
- 使用 Jitter,避免同步脉冲。
- 尊重
Retry-After。 - 副作用具备幂等或状态确认。
- 监控原始请求与 Retry 请求比例。
- 多层调用只指定一个主要 Retry Owner。