重试既是"稳定手段",也是"成本放大器"
重试能救回瞬时故障 ✔,但也会放大成本与延迟 ✗,甚至把上游彻底压垮 ✗(重试风暴 ✔)。
所以"该不该重试"要靠规则 ✔,而不是"失败就再来一次" ✗。
一、先分清四类失败
| 类型 | 例子 | 该不该重试 |
|---|---|---|
| 瞬时故障 | 连接重置、5xx、超时 | 该(退避重试 ✔) |
| 限流 | 429 / 配额超限 | 该(按建议等待,别硬顶 ✔) |
| 参数错误 | 400、schema 不符 | 不该(重试还是错 ✔) |
| 业务拒绝 | 权限不足、内容不合规 | 不该(重试是骚扰 ✔) |
分不清这四类,重试就是赌博 ✗。
二、重试的三条硬规则
- 指数退避 + 抖动(1s → 2s → 4s × 随机抖动 ✔)—— 避免所有客户端同时重试 ✔;
- 上限要硬(次数 ≤ 2~3、总时长 ≤ 10~15s ✔)—— 超过就交给降级链 ✔;
- 只在幂等操作上重试 ✔(写操作要么幂等,要么先补偿再重试 ✔ —— 见接线那篇 ✔)。
三、绝对不要重试的三种情况
- 参数类错误(错误已明确 ✔);
- 已经超过总预算(成本或时间 ✔);
- 上游整体故障(此时重试只会加重拥塞 ✔)→ 应该走降级/熔断 ✔。
四、重试要可观测(最容易被忽略)
- 重试次数与成功率(重试后成功占比 ✔ —— 很低说明重试没意义 ✔);
- 重试带来的额外成本占比 ✔(常占 10%~30% ✔ —— 见成本可视化那篇 ✔);
- 重试集中度(是否集中在某个上游/某类请求 ✔)。
要点:重试成本必须单列 ✗,否则它会被平均进总成本里看不见 ✔。
五、一个务实配置(可直接抄)
网络/5xx:重试 2 次,退避 1s/3s + 抖动,总上限 10s
限流(429):按 Retry-After 等待,最多 1 次
4xx 参数类:不重试,直接回错(带可读原因 ✔)
写操作:默认不重试;幂等写可重试 1 次,且用幂等键 ✔
熔断:错误率 > 50% 且持续 30s → 熔断 60s,半开后放 10% 探测
一条判据
你的重试额外成本占总成本多少 ✔?说不出来,说明重试既没被观测、也没被控制 ✗。