一次超时只表示“调用方没等到结果”,不表示服务端没执行。安全的调用策略必须把截止时间、有限重试和幂等一起设计,否则重试会把短暂抖动放大成全站故障。
Table of contents
五个术语是一条链
- 超时:一次等待的上限。
- 截止时间:整条请求剩余的总时间,比每跳独立超时更可靠。
- 重试:再次尝试可能恢复的操作。
- 幂等:同一操作执行一次或多次,最终业务效果相同。
- 去重:用请求标识识别重复,是实现幂等的一种办法。
客户端超时后,付款服务可能已经扣款,只是响应丢了。因此写请求携带 Idempotency-Key,服务端在同一事务中保存键、请求摘要和结果。重复请求若摘要不同,应返回冲突而不是复用旧结果。
只重试可恢复故障
可以重试连接重置、限流和部分 5xx;不要重试参数错误、权限错误和确定性的库存不足。每次尝试还要满足“剩余截止时间足够”。建议设置最大尝试次数,并使用指数退避加随机抖动(jitter):
上限 = min(最大间隔, 初始间隔 × 2^(失败次数-1))
实际等待 = random(0, 上限)
随机抖动让大量客户端不会同时醒来。服务端若返回 Retry-After,应优先尊重它。整条链路只能由一层主动重试;网关、SDK 和业务服务同时重试 3 次,最坏会放大到 27 次。
运行故障实验
在分布式系统实验室下载执行:
mvn -Dtest=DistributedMechanismsTest#retryStopsAfterSuccessAndUsesBoundedBackoff test
测试让前两次调用返回失败,第三次成功,同时验证等待上限不超过 150ms。教学模型不真的休眠,生产实现还应记录尝试次数、最终原因、剩余预算和幂等命中率。
常见错误是把数据库唯一键冲突吞掉后直接返回成功。正确做法是查询已保存结果并校验请求摘要;否则两个不同请求误用同一键时会掩盖数据错误。
下一步
继续阅读08-03 分布式 ID:趋势递增、时钟与号段,学习怎样为幂等请求和业务实体生成跨节点标识。