重试与幂等
网络不可靠,重试是补偿手段;重试可能造成重复,幂等是补偿的补偿。面试必考组合:退避重试 + 唯一键幂等。
超时控制
重试的前提是超时:请求不能无限等。
| 超时 | 含义 | 经验值 |
|---|---|---|
| 连接超时 | 建立连接的最长等待 | 数百毫秒到 1 秒 |
| 读超时 | 等待响应的最长时长 | 按业务 RT 定(如 3 秒) |
| 总超时 | 整个调用链的上限 | 层层递减(调用方 < 被调用方) |
要点:超时层层递减(A 调 B 的读超时小于 B 自己的处理上限),否则上游超时了,下游还在处理,请求堆积。没有超时的调用是雪崩的引线。
重试策略
| 策略 | 机制 | 问题 |
|---|---|---|
| 立即重试 | 失败马上再来 | 下游没恢复,白重试 |
| 固定间隔 | 等固定时间重试 | 简单,但重试风暴风险 |
| 指数退避 | 间隔翻倍(1s、2s、4s…) | 标准方案 |
| 指数退避 + 抖动 | 间隔加随机值 | 防重试请求同时到达(惊群) |
指数退避: delay = base × 2^attempt + jitter
例: 1s, 2s, 4s, 8s, 16s (上限 30s, 最多 5 次)关键规则:
- 只重试可重试的错误:超时、连接失败、5xx 可重试;4xx(参数错)重试无意义
- 设最大次数:无限重试 = 无限放大故障
- 重试风暴:大量客户端同时重试,打爆刚恢复的下游。抖动 + 上限 + 熔断配合(见熔断篇)
幂等
幂等:同一操作执行多次,结果与执行一次相同。为什么需要:重试、MQ 重复投递(at-least-once)都会产生重复。
| 方案 | 机制 | 适用 |
|---|---|---|
| 唯一键去重 | 请求带唯一 ID,处理前查重 | 通用(下单、支付) |
| 数据库唯一约束 | 业务唯一字段建唯一索引 | 天然防重(订单号) |
| 状态机幂等 | 状态只允许单向流转 | 订单状态流转 |
| 版本号/乐观锁 | 带版本更新,版本不符拒绝 | 并发更新 |
下单幂等流程:
1. 客户端生成 request_id(UUID)
2. 服务端查幂等表: 已存在 → 返回原结果
3. 不存在 → 处理业务 + 写幂等表(唯一键)
4. 重复请求命中唯一约束 → 返回原结果注意:幂等表写入要原子(唯一索引兜底,查了再写有竞态);响应要缓存(重复请求返回首次结果,而不是“已存在”报错)。
面试追问
- 为什么重试要退避? 下游故障不会瞬间恢复,立即重试是浪费且加重负载。指数退避 + 抖动防重试风暴
- 哪些错误可重试? 超时、连接失败、5xx。4xx 重试无意义,别重试
- 幂等怎么做? 唯一键 + 幂等表(唯一约束兜底)、数据库唯一索引、状态机。消费端幂等是 MQ 场景的标配
- 重试和幂等什么关系? 重试产生重复,幂等消化重复。重试必须有上限,幂等必须有原子保证
- 超时怎么设计? 层层递减,调用方超时小于被调用方处理上限。总超时兜底