不可靠网络:超时、重试与幂等性
本文基于模型知识整理(生成时未联网核对),关键结论建议对照经典文献复核。
一句话定义
网络会丢失、延迟、乱序、重复你的消息,且"请求没到"与"响应没回来"无法区分,因此一切远程调用都必须配超时与重试,而重试的安全前提是被调操作幂等。
为什么重要
这是分布式系统最日常也最容易写错的部分:几乎每个生产事故复盘里都有"超时设错 / 重试放大故障 / 重试导致重复扣款"的身影。掌握"超时—重试—幂等"三角,是从写单机代码转向写分布式代码的第一道坎。
前置知识
kp-005(故障不可判定性)、kp-002(延迟分位数)。
核心概念
- 调用结果三态:成功 / 失败 / 未知(timeout)。未知态是分布式编程区别于单机编程的根本:你不知道操作是否已生效。
- 超时(timeout):放弃等待的时间上界。设短了误判活节点为死节点;设长了故障被"拖长",恢复变慢。
- 重试(retry):对失败与未知结果重新发起。策略包括固定间隔、指数退避(exponential backoff)、加抖动(jitter)。
- 幂等(idempotency):同一操作执行多次与一次效果相同。是重试安全性的前提。
- TCP ≠ 可靠投递:TCP 保证的是"连接内字节有序到达或报错",不保证业务消息被处理、也不保证对端服务可用。
原理与机制
为什么未知态无法消除:请求发出后,回答"对方是否执行了"需要对方的响应,而响应本身可能丢——于是要再问,再问又可能丢,归纳下去永远有残留的不确定性(与 kp-017 的 FLP 精神一致)。工程结论:把"未知"当作一等公民设计——要么操作幂等(重试无害),要么携带唯一 ID 让对端去重(idempotency key),要么用版本号让重复执行自动失效(fencing token,见 kp-021)。
超时如何选:经验起点是略高于下游 P99 延迟(kp-002),并结合"故障要多久被发现"的业务预算。两个方向都要防:太短 → 在流量毛刺时大规模误判,触发重试风暴;太长 → 单个慢节点把整个调用链拖死(线程池占满、级联超时)。
重试的三宗罪:① 放大流量(下游卡顿时重试流量可成倍涌入,压死最后的恢复能力);② 重复副作用(非幂等操作重复扣款、重复下单);③ 延迟叠加(上层重试包着下层重试,P99 爆炸)。对策见 kp-029。
图示
Client ──request──► Server (执行了,但响应丢失)
Client ◄──✕─────── Server
Client 观察到 timeout:执行了?没执行?→ 无法区分
→ 只能重试 ⇒ 前提:操作幂等 或 携带去重 key
直观类比
给朋友打电话没人接:可能是他在忙,可能是手机没电,也可能信号不好。你只能等一会儿再打(退避重试),并且拜托他"同一件事我可能说两遍,你听到了别重复办"(幂等去重)。
实例或案例
- 支付回调幂等:第三方支付网关会重发回调通知,商户侧必须以订单号+状态做幂等表,否则重复入账。
- 指数退避 + 抖动:AWS 各 SDK 默认策略;无抖动时所有客户端同步重试,形成周期性流量尖峰(thundering herd)。
- HTTP 语义:GET/PUT/DELETE 天然幂等,POST 天然不幂等——这正是 REST 语义设计与网关重试策略联动的原因。
常见误区
- 误区一:"TCP 可靠所以业务不会重复/丢失"。TCP 只管连接内字节,应用层重试、代理重发、对端处理成功但响应丢失,都会造成业务级重复。
- 误区二:"超时设长一点保险"。超时是与故障检测速度、线程池占用的直接权衡,"保险"的超时经常是级联故障的帮凶。
- 误区三:"重试加上了就完事"。没有退避与抖动的立即重试等于 DoS 自己;没有上限的重试会把瞬时故障变成持续流量。
与其他知识点的关系
- kp-011:主从复制中写确认的超时决定降级/切换策略。
- kp-019:Raft 的心跳超时与选举超时是活性来源,但正确性不依赖超时精确。
- kp-029:本章是"是什么",kp-029 是工程化的完整清单(去重窗口、重试预算、背压)。
自测题
- 远程调用为什么存在"未知"结果?能不能彻底消除?
答:响应可能丢失且无法与"未执行"区分,追问又依赖可能丢失的响应,因此不确定性只能被收敛不能被消除;工程上靠幂等/去重把它变得无害。
- 幂等性为什么是重试的前提?
答:重试的触发条件包含"结果未知",意味着操作可能已经执行过;只有幂等才能保证重复执行不产生额外副作用。
- 设计超时时应考虑哪些因素?
答:下游延迟分布(≥P99 留余量)、故障发现时限的业务预算、上游线程/连接占用成本,并区分连接超时与读超时。
延伸阅读
- Martin Kleppmann《DDIA》第 8 章。
- Marc Brooker, "Timeouts for Distributed Systems"(AWS Builders' Library)。
- Exponential Backoff And Jitter(AWS Architecture Blog)。