摘要

  • RFC 2188 的 ESRO 把可靠的无连接远程操作建立在 UDP 等不可靠无连接传输之上,并以 acknowledged-result 与 non-acknowledged-result 两种模式,在消息开销和双方对结果的知识之间作取舍。
  • acknowledged-result 模式中,performer 的 FAILURE.indication 并不能可靠证明 invoker 没有得到成功结果:结果报文丢失、ACK 丢失以及本地 provider 故障会形成不同事实,却可能在 performer 一侧收敛为失败观察。
  • non-acknowledged-result 模式进一步削弱了 performer 对远端状态的知识:RESULT.confirm 或 ERROR.confirm 不包含来自 invoker 的新增确认,只意味着本地协议操作结束。
  • Invoke ID、timeout、重传耗尽和协议 confirm 都属于有限范围的协议证据。它们不能单独证明身份认证、幂等性、不可重复执行、持久业务提交或后续账务一致性。

一份追求低开销的 Informational 协议

RFC 2188 于 1997 年 9 月发布,类别为 Informational。文件明确说明它不规定任何 Internet Standard。IESG 注记还特别指出,该协议没有经过 IETF 工作组审查,初步检查已经发现可能具有重要可扩展性影响的问题,考虑部署者应自行分析这些影响。这个身份边界很重要:研究 ESRO 的历史意义,不应把它重新包装成当时互联网的通用标准答案。

RFC 2188 定义的 Efficient Short Remote Operations,简称 ESRO,目标是在 UDP 或其他不可靠、无连接的传输服务之上,以较小开销提供可靠的无连接远程操作。设计明确把 CDPD 一类无线网络纳入考虑。协议支持分段与重组、串接与分离,以及面向应用的复用;使用 UDP 时,RFC 2188 指定端口 259。

这些机制服务于一个简单的远程操作模型。发起操作的一方是 invoker,尝试执行操作并报告结果的一方是 performer。同一个实体在不同操作中可以承担不同角色,因此这两个词描述的是一次远程操作中的角色,而不是永久固定的客户端和服务器身份。

RFC 2188 同时定义了两种结果处理方式:基于三次握手的 acknowledged-result 模式,以及基于两次握手的 non-acknowledged-result 模式。文档自己把二者的差异描述成效率与可靠性之间的权衡,而这里所谓可靠性的一部分,正是双方对另一端是否成功或失败能够知道多少。

因此,少一条消息并不只是少几个字节。它也意味着少一个能够改变端点知识状态的协议事件。

一个操作,不是一条原子化历史记录

分析 ESRO 时,最容易造成误判的方法,是把“远程操作”想成数据库里只有一个最终状态的记录:要么成功,要么失败,然后认为协议两端应该同时知道那个答案。

线上真实发生的事情至少可以拆成一串不同事件:

  1. invoker 的应用请求发起调用;
  2. invoker 的 provider 构造并发送 INVOKE PDU;
  3. PDU 被网络传输、丢失或重复;
  4. performer 的 provider 接收并向 performer 应用递交 invoke indication;
  5. performer 执行、拒绝或无法处理该操作;
  6. performer 构造 result、error 或 failure;
  7. RESULT/ERROR PDU 被发送;
  8. invoker 是否收到该 PDU;
  9. invoker 是否向应用产生 RESULT.indication 或 ERROR.indication;
  10. 在确认模式下,ACK 是否产生并返回;
  11. performer 是否收到 ACK,并据此形成 RESULT.confirm、ERROR.confirm 或 FAILURE.indication;
  12. 应用自己的业务状态是否已经持久化;
  13. 后续系统是否完成对账、去重、补偿或验证。

协议不能把这些步骤压缩成一个无条件共享的事实。某个端点实际观察到的是其中若干事件,而不是完整时间线。

这正是 RFC 2188 中最有价值的证据边界。

三次握手:ACK 增加知识,但没有制造全知

在 acknowledged-result 模式下,performer 发送 result 或 error 后,需要 invoker 的 ACK。

RFC 2188 对 performer 一侧的语义定义得很清楚:只有结果或错误得到 invoker 确认后,performer 才产生 RESULT.confirm 或 ERROR.confirm。换言之,这个 confirm 并不只是“我发送完了”,而是依赖一个来自远端的新增协议证据——ACK。

但反方向并不成立:没有收到 ACK,并不能可靠推出 invoker 没有收到结果。

假设 performer 已经完成操作并发出 RESULT PDU。至少存在两种不同的网络历史:

  • RESULT PDU 丢失,invoker 从未见到结果;
  • RESULT PDU 成功抵达,invoker 得到结果并发出 ACK,但 ACK 在返回途中丢失。

从 performer 的有限观察位置看,这两条历史可能变得不可区分。重传最终耗尽后,它可能只能看到 FAILURE.indication。

第二种情况下,invoker 已经拥有 RESULT.indication,甚至已经把操作当作成功;performer 却仍可能报告失败。于是“performer 看见 FAILURE”与“invoker 没有收到成功结果”并非同一个命题。

RFC 2188 自己直接指出这种 FAILURE.indication 并不 robust,因为从 invoker 的视角操作可能已经成功,并提出一种补偿办法:performer 可以用另一个独立操作来验证这次 FAILURE。

这个建议非常关键。它没有试图让 timeout 承担超出自身能力的证明责任,而是引入新的通信,获取新的证据。

FAILURE 证明的是观察路径,不是完整现实

这也解释了为什么故障名称尤其容易误导监控系统。

“FAILURE”在界面上看起来像最终结论,但协议事件的名称不能替代其生成条件。在 acknowledged-result 模式下,performer 的 FAILURE.indication 可以与 result/error 未被确认相关,也可以来自 performer 侧本地 provider 故障。即使只看协议导致的确认失败,RFC 也明确区分不了 RESULT/ERROR PDU 丢失和 ACK PDU 丢失。

因此 performer 的 FAILURE 更准确地回答:

在本端可见的确认过程和本地 provider 状态下,我没有获得足够证据形成成功 confirm。

它并不自动回答:

远端应用绝对没有得到结果。

RFC 2188 同时说明,在 acknowledged-result 模式中,如果 invoker 收到 FAILURE.indication,performer 侧也有 FAILURE.indication。这是该模式中特定的状态关系,却不能进一步推广成“所有结果和失败都在两端对称存在”。

局部存在某个蕴含关系,不等于整个协议建立了一个单一共享状态机。

两次握手:confirm 的含义明显更弱

non-acknowledged-result 模式把这种差异表达得更直接。

这一模式没有让 invoker 对 result/error 再作确认。RFC 2188 因而说明,performer 一侧的 RESULT.confirm 和 ERROR.confirm 是在没有收到来自 invoker peer 的额外信息时产生的;这些 primitive 没有关于远端状态的协议含义,只表示 operation 的结束。

这里的 “confirm” 与三次握手中的同名事件拥有明显不同的证据强度。

三次握手的 performer RESULT.confirm 至少意味着:相关 ACK 已经回到 performer。

两次握手的 performer RESULT.confirm 则不能说明:invoker 收到了 result、invoker 的应用接受了 result,更不能说明某个更高层业务流程完成了。

在 ESRO 的服务定义层面,non-acknowledged-result 模式不会由协议在 performer 一侧产生 FAILURE.indication;performer 一侧能够形成 FAILURE.indication 的情况是本地 service provider 故障。invoker 一侧则可能因为没有收到 peer 的 result、error 或 failure,或者因为本地 provider 故障而产生 FAILURE.indication。

所以即使 invoker 最终看到 FAILURE,也不能倒推出 performer 应用从未执行过操作。它首先说明的是 invoker 没有获得所需的返回证据,而不是远程副作用不存在。

“没收到答案”与“没发生事情”是两个问题

这是远程操作最危险也最容易被抹平的区别。

设想一个具有不可逆副作用的操作。INVOKE 成功抵达 performer,performer 执行了操作,但返回的 RESULT 丢失。invoker 只看到 timeout 或最终 failure。

此时至少有三个不同问题:

  • 协议是否完成?
  • performer 是否执行?
  • 业务状态是否已经永久改变?

第一个问题的答案不能自动决定后两个。

反过来也一样。收到 RESULT 说明的是某条协议结果路径已经到达 invoker,并不凭空证明应用之外的所有后续状态都已持久化,更不能证明另一套账本、外部资源或后续异步流程已经完成。

因此,业务系统若把 transport 或 remote-operation timeout 直接解释为“安全地重新执行一次”,就跨越了证据边界。

对于幂等操作,这种跨越可能只制造额外流量;对于非幂等操作,它可能产生重复扣减、重复创建、重复发送或其他不可逆副作用。

Invoke ID 是关联机制,不是业务真相证明

RFC 2188 的 Invoke-ID 用来标识一次 invocation,并把它与对应的 RESULT、ERROR 和 FAILURE 关联起来。它也允许同时进行的异步操作彼此区分。

这是一项重要机制,但其证明范围非常窄。

Invoke ID 能说明“这些协议 primitive 属于同一个 invocation 上下文”,却不能单独证明:

  • invoker 已通过认证;
  • invoker 被授权执行操作;
  • 两个具有不同 Invoke ID 的调用不是同一个业务意图的重复提交;
  • performer 实现了幂等处理;
  • 某个业务事务已经持久提交;
  • 外部副作用仅发生一次;
  • 两端业务数据库最终一致。

RFC 对 Invoke-ID 的复用规则也属于协议关联和生命周期管理,而非全局业务身份。一个应用若需要“同一笔业务只执行一次”,仍需要由应用定义稳定的业务键、去重策略或幂等机制,而不能把短期协议 correlation ID 当成业务唯一性本身。

类似地,RFC 2188 明确把远程操作参数的 presentation encoding 机制放在文档范围之外。ESRO 可以标识所用编码类型,但它没有因此定义参数本身的全部应用语义。

协议知道怎样运送一个操作,并不等于协议知道那个操作在业务世界里意味着什么。

timeout 是部署参数,不是远端状态探针

RFC 2188 没有给 timeout 赋予神秘的真值发现能力。

它列出多组需要根据实际网络特性选择和优化的参数,包括:

  • INVOKE PDU 的重传间隔;
  • RESULT/ERROR PDU 的重传间隔;
  • 最大重传次数;
  • inactivity time;
  • reference-number lifetime。

这些数值的正确选择依赖所使用的网络。

这意味着一次 timeout 首先是“在本地配置的某段等待窗口内,没有看到所期待事件”。它同时受实际 RTT、链路丢包、拥塞、无线环境、处理延迟、重传参数和本地实现影响。

把它直接翻译成“远程操作没有执行”,相当于把部署者选择的时间阈值冒充为远端业务数据库的查询接口。

更稳妥的模型是:timeout 会降低某个端点对结果的确定性,并可能触发验证、查询状态、人工处理或受幂等保护的恢复机制;它本身不产生额外的远程业务证据。

协议没有认证机制

另一个必须与可靠性交付拆开的边界是认证。

RFC 2188 明确说明 ESROS 本身没有 authentication mechanism。认证属于 performer 的责任,并且位于 ESROS 范围之外;未经认证时,performer 不应接受 invoker。

因此,“我收到了一个语法正确且关联正确的 ESRO invocation”和“我知道对方是谁并授权它执行该操作”完全是两层事实。

同样,“重传最终成功”不能替代认证;“Invoke ID 匹配”也不能替代授权。

这对历史理解尤其重要。可靠传输、身份认证、授权、幂等和业务提交是彼此不同的控制层。后来系统经常把它们共同包装进一个 API 调用体验,但协议栈下方仍然不存在一条自动把这些事实焊接在一起的因果链。

RFC 1831 是相邻对照,而不是一部“RPC 通史”

RFC 2188 自己将 ESRO 与当时已有的远程调用模型进行比较,并引用 RFC 1831 的 ONC RPC。这个对照可以帮助限定 ESRO 的设计选择:二者都围绕远程调用组织请求和结果,但 ESRO 特别强调低开销和其自身的可靠无连接协议过程。

RFC 1831 也提醒读者,远程调用与本地过程调用并不等价:网络和远端服务器故障需要单独处理,认证也可能成为必要问题。

但这一相邻关系不应被扩大成“ESRO 代表 RPC 的普遍历史”或“RFC 1831 能替 ESRO 证明所有远程调用语义”。二者是不同协议文本,各自拥有自己的范围和机制。这里有用的是比较边界,而不是建立虚假的单线演进史。

RFC 2524 提供的是分层应用证据

1999 年的 RFC 2524 描述 Efficient Mail Submission and Delivery,并明确把 ESRO 作为其下层机制使用。文件规定 EMSD-P 使用 ESRO 的两次和三次握手服务,并在其协议层次中利用 ESRO 提供的可靠无连接远程操作能力。

这可以证明两件有限而具体的事情:ESRO 的设计确实能够被后续协议组合使用;高效邮件提交与交付是 ESRO 预期应用方向的一部分。

它不能单独证明 ESRO 后来获得普遍部署,不能证明某个商业市场接受它,也不能证明这种架构在今天仍具有特定性能、安全或合规优势。

历史证据必须保持与来源强度匹配。规范中的使用关系证明协议设计和分层关系;它并不会自动升级成市场成功史。

从协议史中留下“谁知道什么”

RFC 2188 今天仍值得研究,并不是因为每个系统都应重新采用 ESRO,而是它把一种经常被现代抽象隐藏起来的事实写得格外明显。

在网络另一端发生的事情,必须通过事件留下证据。

发送 INVOKE 是一个证据。收到 INVOKE 是另一个。performer 执行操作又是另一个。RESULT 被构造、发送、收到,各自不同。ACK 是新的证据。RESULT.confirm 是基于特定前提形成的局部状态。timeout 与 FAILURE 也都有自己的生成边界。

认证、授权、幂等、持久提交和最终对账则位于更高层,需要额外机制证明。

因此,当我们说某次远程操作“可能已经成功”时,这里的“可能”并不是声称业务提交已经发生。它仅意味着:现有协议观察不能排除那条历史,performer 或 invoker 缺少决定性的协议证据。

这一区别是 RFC 2188 最值得保存的互联网史:分布式系统的问题不仅在于事件会失败,还在于不同观察者会以不同证据认识同一个事件。