Post

RPC 失败语义:超时、重试与幂等

理解超时为什么不能证明请求未执行,以及至少一次、至多一次和幂等设计的边界。

RPC 失败语义:超时、重试与幂等

RPC 让远程调用看起来像本地函数,但调用路径中的网络、进程和存储仍可能失败。本文从分布式系统课程笔记中整理,重点讨论“没收到回复”到底意味着什么。

一次调用经过的路径

客户端 stub 序列化参数,经网络发送;服务端 stub 解析请求并调用业务函数,再把返回值送回客户端。IDL 和共同的线格式解决跨语言、字节序和数据表示的问题,却不能消除远程故障。

现象可能的原因
客户端等不到响应请求丢失、服务端缓慢、服务端崩溃、响应丢失
重试后收到结果本次执行成功,或服务端返回了已缓存的结果
服务端已执行,客户端仍报错执行和获知执行结果之间出现故障

超时首先表示调用方在期限内没有获得结果,不能仅凭它判断操作是否发生。

至少一次与至多一次

重传可以提高请求最终被处理的机会,但也可能导致重复执行。至少一次语义允许重复;它的交付保证仍依赖最终可达、持续重试等前提,有限次重试不保证一定执行。

至多一次通过请求标识与去重状态限制重复执行,却允许请求一次也没执行。服务端崩溃后是否仍能识别旧请求,取决于去重记录是否持久保存,而不是只看请求 ID 是否唯一。

幂等设计要覆盖业务效果

把某条记录设置为指定值,通常比无条件增加计数更容易重试。幂等键提供了另一种方式:相同业务请求使用相同键,服务端检查并保存对应结果。

例如“为订单创建一张配送单”,可要求同一个订单请求键只对应一个配送单。关键是去重检查、业务修改和结果保存应具有一致的原子性。若先创建配送单、再保存去重记录,中间崩溃仍可能导致重复。

还要考虑键的作用域、过期时间,以及同一个键携带不同参数时如何处理。仅给请求加字段,不足以完成幂等设计。

Exactly-once 要明确范围

一般网络中的一次 RPC 无法让客户端在所有故障下都确定执行结果。某些系统可以在限定事务或日志范围内保证效果只发生一次,但必须说明外部副作用是否也在保证范围内。

重试也是负载

服务已经过载时,立即反复重试可能放大压力。实践中需要重试预算、退避、抖动和截止时间,同时区分可重试与不可重试的错误。具体实现可参考 gRPC 的重试文档。

接口评审时,我会先问:重复调用会产生什么效果?超时后如何查询状态?崩溃恢复后还保留哪些去重信息?

This post is licensed under CC BY 4.0 by the author.