摘要

  • David Cheriton 在 1988 年 2 月的 RFC 1045 中规定了实验性 VMTP,用于远程过程调用一类请求/回复事务。客户端标识、32 位事务编号和转发计数一起定位一笔事务,服务器保存状态以识别重复请求。
  • 标记为幂等的回复丢失后,客户端重发请求,服务器重新执行并产生新答复;需要保留原结果的回复则由服务器存副本,或等确认后再放手。问时间的例子说明:重新看钟,可能比取出旧答复更有用。

“重试”两个字听上去像一条简单指令,执行起来却得先回答:第一次到底做完了什么?在 David Cheriton 的 RFC 1045 里,VMTP 被设计成处理请求和回复的传输协议,远程过程调用是典型用途。底层数据报可能丢包,服务器也可能已经完成工作却没能让客户机收到答复。第二次请求到来时,协议必须分清自己面对的是新工作,还是一张迟到的旧工单。

VMTP 为工单编了号。在一个实体域内,客户端标识、事务编号和 ForwardCount 共同构成事务坐标。32 位事务编号在客户端实体创建时从随机值起步,通常每完成一笔就递增。四位转发计数只有在请求被转送时才改变。服务器按客户端保留状态,看到相同坐标的请求,就能把它认作重复件。编号不是装饰;它决定一台机器会不会被同一张工单叫醒两次。

保存状态也有期限。服务器发出回复后,通常继续留着记录一段时间,以挡住网络里慢吞吞的重复请求。若一条请求到来时记录已经不在,RFC 给出几条路:通过 Probe 向客户端管理模块查询现有事务编号;在服务器未重启、记录覆盖包最大寿命和重试时段的条件下推断它是新请求;或者确认操作可以安全重做。一个“再试一次”的按钮,背后竟是对旧包寿命、机器重启和业务动作的三重盘问。

答复丢失后,真正有趣的分岔来了。若回复带有幂等标记,VMTP 不保存那份旧答复。客户机重发 Request,服务器将最近一笔事务的请求再交给应用,应用重做操作并生成新回复。该客户端更早的迟到请求会被丢弃。Cheriton 用查询当前时间说明好处:第一次的钟点已经过去,重新看一眼才叫“现在”。把旧时间认真归档,反倒可能是最敬业的错误。

另一条路专为需要保留原结果的操作准备。若回复不能这样重做,VMTP 模块必须留存答复副本,可以实际复制,也可以写时复制;或者让服务器等到确认后才继续。这里付出的代价是内存或等待。读钟可以再来一次;修改账本一类动作若执行两遍,账本会给你另一种惊喜。幂等标记的重点在操作本身,而不是数据包看起来有多整齐。

协议还精打细算地处理普通重传。在低丢包环境里,一次重传可以先只送消息控制块,接收方再按需索取缺失的数据段。后续请求也可能在协议意义上确认较早的回复,让服务器释放状态。这些细节把三种费用写在同一本账里:保存答复所占的空间、重发数据所用的带宽、重新执行所花的时间。VMTP 要求实现者为每种工作选相应的账单。

这篇 RFC 是一份实验性协议规格,最耐看的地方不是某个华丽的字段,而是它给“丢了答复”这件事分了责任。服务器得记住自己见过谁;应用得知道哪些工作能够重做;客户端得按事务重试。问钟时,重新问一次既省抽屉又更准确。到了会改变现实的操作,旧答案就值得一格抽屉,甚至值得让服务器停下来等一句确认。