Skip to content

通信模式 ​

Category ​

networking / IPC。节点定位:按语义分通信模式(等不等结果、接收方是否唯一、是否广播、失败怎么算),再谈组件。

Definition ​

系统边界一旦明确,下一步就不是“用哪个中间件”,而是“这条边界到底该采用什么通信模式”。通信模式选错,后面即使用最成熟的组件,也只是把错误稳定地放大。

真正有价值的区分,不是按产品名词分,而是按语义分:调用方是不是要等结果,接收方是不是唯一,消息是不是天然广播,失败以后是立即报错、稍后补偿,还是允许静默重试。

Problem ​

「用哪个中间件」是最后才该回答的问题。模式归类错了,再成熟的组件也只是把错误稳定地放大:该等结果的地方用了 fire-and-forget,结算就变成玄学。

最常见的几类通信模式 ​

同步请求-响应 ​

这是最直观的一类模式。调用方发起请求,等待明确结果,再决定下一步。它适合:

  • 玩家操作必须立即反馈
  • 上下游必须共享同一次交互语境
  • 出错需要立刻终止当前流程
  • 调用结果直接影响当前主链路

它的优点是语义清楚,调用方最容易理解;缺点是耦合强、延迟直接暴露、下游抖动会立刻传到上游。

异步投递 ​

调用方把任务或事件投递出去,不同步等待处理完成。这适合:

  • 调用方更关心“交付出去”,而不是“立刻处理完”
  • 下游可以稍后消费
  • 需要削峰或缓冲
  • 链路允许重试和补偿

它的优点是解耦和缓冲能力强;缺点是链路变得不那么直观,排障、顺序、重复处理和回执确认都会更复杂。

发布订阅 ​

某个事件发生后,多个订阅者都可能需要知道并做各自处理。例如战斗结束后,战绩、排行、成就、报表、通知都要被触发。这种模式适合:

  • 一个事实要被多个系统复用
  • 上游不想知道具体有哪些下游
  • 下游处理节奏可以彼此独立

它的代价是事件契约需要长期稳定,否则一次字段或语义变化会同时影响多个消费者。

请求接收 + 后台完成 ​

这是一种很常见的混合模式。入口先同步确认“请求已受理”,真正的重操作在后台异步完成。它适合:

  • 玩家需要立即得到受理反馈
  • 后台处理耗时较长
  • 最终结果可以稍后查询或推送

例如资产导入、排行榜重算、大型结算归档、离线补发等。它的关键不是模式本身,而是系统要把“受理成功”和“处理成功”明确区分。

模式选型首先要看什么 ​

是否必须共享同一个交互时刻 ​

玩家点击按钮后,如果必须立刻知道成功或失败,说明更偏同步。如果调用方只需确保事件被可靠接受,下游什么时候完成并不影响当前交互,就更偏异步。

下游是否只有一个 ​

一个请求只对应一个明确处理者时,同步调用往往更简单;一个事实需要多个下游同时感知时,事件或发布订阅通常更自然。

失败是否必须立刻暴露 ​

有些失败必须在当前交互里返回,例如进入副本失败、购买失败;有些失败可以延后恢复,例如报表汇总失败、通知发送失败。失败暴露时机不同,通信模式通常也不同。

系统能否承受积压和延迟不确定性 ​

异步系统的代价不是“更慢”,而是“完成时刻不确定”。如果业务根本承受不了这种不确定性,就不应该为了所谓解耦把它消息化。

常见模式组合 ​

真实项目里,通信模式通常不会单独存在,而会组合出现:

  • 同步入口 + 异步后处理
  • 命令调用 + 事件广播
  • 本地同步推进 + 跨系统异步联动

例如房间结算可能先同步产出权威结果,再异步广播给排行、成就和报表系统。这样做的好处是主链路仍然可解释,外围链路又能获得解耦。

通信模式变化,本质上是在移动复杂度 ​

很多团队误以为把同步调用改成异步,就等于把复杂度降低了。实际上你只是把复杂度从“调用时等待”移动成了:

  • 回执确认
  • 重试逻辑
  • 消息顺序
  • 消费幂等
  • 补偿与审计
  • 延迟观测与积压治理

所以通信模式不是“同步老旧、异步先进”,而是“复杂度应该在哪一层被承担”。

常见误区 ​

最常见的错误,是把异步理解成架构升级,遇到链路就先消息化。第二类错误,是把一个事实同时当命令和事件使用,导致调用方既想同步拿结果,又假装自己是解耦的。第三类错误,是只在设计时画模式图,不去补充确认语义、失败语义和可观测性,最后所有模式都变成不稳定的混合体。

实战案例:一次「兑换码点了没回执,玩家连点六次」的通信模式错配排查 ​

案例为教学示例(非摘自真实项目),数字经过取整简化。它把本页「模式选型首先要看什么」「请求接收 + 后台完成」「通信模式变化,本质上是在移动复杂度」用到深水区:该等结果的地方用了 fire-and-forget——这是兑换回执的悬空排查,也是「模式归类错了,再成熟的组件也只是把错误稳定地放大」的完整展开。

背景与现象:某发行平台把兑换码链路从同步请求-响应改为 fire-and-forget 投递,口号是「全面异步化」。改造前提交到回执 P99 240 毫秒;改造后玩家点「兑换」界面无回执,平均 4.1 秒才刷新,1.2% 的兑换请求无回执悬空;玩家以为没成功反复提交,单人最多连点 6 次,重复提交工单日均 260 起。

第一步:现象与口径——回执、重复、悬空三笔账 ​

回执口径:玩家点击「兑换」→ 界面得到明确的成功或失败,才算这次交互完成。三笔账分开记:

  • 回执账:改造前 P99 240 毫秒,改造后平均 4.1 秒——玩家看到的是「处理中」,心里想的是「是不是没成」。
  • 重复账:无回执逼出重复提交,工单日均 260 起,单人最多连点 6 次。
  • 悬空账:1.2% 的请求投出去既没回执也没失败记录,对账只能人工捞。

第二步:分层归因——按本页四问逐项对 ​

text
兑换回执失控归因  占比
  fire-and-forget 用在必须共享交互时刻的链路    55%
  「受理成功」与「处理成功」未区分,回执无人负责    30%
  复杂度搬家六项无人承接,回执与重试悬空    15%

逐项比对:第一问「是否必须共享同一个交互时刻」的答案是必须,链路却用了异步投递;「请求接收 + 后台完成」要的受理与处理分离没有做;复杂度搬家清单(回执确认、重试逻辑、消息顺序、消费幂等、补偿与审计、延迟观测与积压治理)一项都没落。三类都在,交互时刻错配那条占比最大。

第三步:根因——模式归类先于组件,顺序反了 ​

本页 Problem 原话:模式归类错了,再成熟的组件也只是把错误稳定地放大——该等结果的地方用了 fire-and-forget,结算就变成玄学。团队先定「全面异步化」再看交互,没回答「是否必须共享同一个交互时刻」,把必须当场出结果的兑换,当成了可以稍后消费的事件。

第四步:处置与回填——把复杂度搬家的账接回来 ​

  1. 模式回退:兑换提交改同步请求-响应当场拿回执;资产导入、排行重算类改「请求接收 + 后台完成」,受理与处理分开。
  2. 搬家清单接管:回执确认、重试逻辑、消息顺序、消费幂等、补偿与审计、延迟观测与积压治理六项逐项落责任人。
  3. 四问进评审:是否共享交互时刻、下游是否唯一、失败是否立刻暴露、能否承受积压,答完再选模式。

复核账:兑换回执 P99 4.1 秒 → 180 毫秒;重复提交工单 260 起/日 → 11 起/日;无回执悬空 1.2% → 0.03%。

回填清单:

回填项动作回填处
交互时刻四问必须共享交互时刻的链路禁用 fire-and-forget模式选型首先要看什么
受理处理分离同步先回「已受理」,后台完成另发结果通知请求接收 + 后台完成
复杂度搬家清单回执、重试、顺序、幂等、补偿、观测六项逐项落位通信模式变化,本质上是在移动复杂度
模式组合基线同步入口加异步后处理为默认,命令与事件分开发常见模式组合

案例的三个教训 ​

  1. 异步不是架构升级,是复杂度搬家——搬家清单没人接,回执和幂等就变成悬空账。
  2. 回执是交互的一部分——玩家点了就要有明确结果,重复提交是缺回执的必然结果,不是玩家的问题。
  3. 模式归类先于组件选择——归类错了,再成熟的组件也只是把错误稳定地放大。

Used By ​

跨服务调用评审;消息中间件选型前的模式归类。

上游:IPC 边界(01)。下游:低延迟与高吞吐取舍(04)、方案选型(06)。关系链:通信模式 → 延迟/吞吐取舍 → 方案选型。

Reference ​

本页为工程经验归纳;页内实战案例为教学示例(数字取整简化)。

游戏知识体系