Skip to content

主链路与外围链路差异 ​

Category ​

networking / IPC。节点定位:主链路(同步、确定性)与外围链路(事件化、可补偿)的判别与纪律。

Definition ​

消息系统最容易被做坏的地方,不是在外围工具链,而是在主链路上被过度滥用。很多团队看到 MQ 能削峰、能解耦,就想把主流程也一起事件化,结果把原本明确的交互过程改造成了一个“最终可能成功、但谁也说不清何时成功”的黑箱。

所以讨论消息系统,必须先把主链路和外围链路分开。

什么叫主链路 ​

主链路通常指那些直接决定当前玩家体验、当前实例状态和当前交互结果的路径,例如:

  • 登录进入游戏
  • 匹配入局
  • 房间内关键操作
  • 副本创建与进入
  • 战斗结束的权威结算

这些链路通常具备几个共同特征:

  • 对延迟敏感
  • 需要明确结果
  • 失败必须及时反馈
  • 往往存在强顺序和强状态依赖

这意味着主链路更看重确定性,而不是最大吞吐。

什么叫外围链路 ​

外围链路通常是围绕主事实展开的后续处理,例如:

  • 战绩归档
  • 排行刷新
  • 成就更新
  • 邮件和推送
  • 运营报表
  • 埋点和审计

这些链路通常允许晚一点完成,允许异步重试,也更适合多消费者并行处理。它们真正追求的是解耦和扩展性,而不是当前交互里的最低时延。

为什么主链路不能简单照搬 MQ 思维 ​

如果把主链路过度消息化,常见问题会立刻出现:

  • 玩家请求已经返回,但系统内部还在排队,不知道最终结果
  • 某个关键消费者积压,主流程语义开始漂移
  • 顺序稍一错乱,运行时状态就不可解释
  • 排障时只能看到“消息发出去了”,却不知道玩家为什么现在还没进房

也就是说,主链路最怕的不是耦合,而是失去确定性。

Problem ​

外围链路为什么反而常常适合事件化 ​

外围链路更适合消息驱动,是因为它们天然具备这些条件:

  • 主事实通常已经产生
  • 多个下游会基于同一事实做不同处理
  • 下游失败不该直接阻断主链路
  • 允许削峰、重试和延迟消费

例如一场战斗已经正式结算,战绩、排行、成就和报表都可以围绕“结算结果已确认”这个事实异步展开。这样既不会让主结算链路承担所有外围成本,也能避免各外围系统反向耦合主流程。

一个更稳妥的拆法 ​

实际工程里,更稳妥的方式通常不是“主链路全同步”或“全异步”,而是分层:

  • 主链路上的关键决定和权威状态同步完成
  • 主事实一旦确定,再通过事件广播给外围系统
  • 外围系统各自按自己的节奏消费、重试和补偿

这类分层的核心是,先定义“哪一步算业务已经成立”,再定义“哪些后续处理可以脱离当前交互时刻”。

最容易做坏的几个点 ​

用消息队列承载实时控制流 ​

如果房间输入、实时同步、关键控制权切换这类事情要穿过 Broker,再好的队列也救不了不适配的链路。

主事实还没落稳,就先广播外围事件 ​

如果上游自己都还没确认最终结果,就先把事件发给一堆消费者,后面一旦回滚,外围系统会全部陷入补偿地狱。

外围系统反向卡住主流程 ​

最常见的形式是,排行、报表、通知这些本该异步处理的东西,被设计成了主链路成功的前置条件。这样主链路和外围链路就又重新绑死了。

一个简单判断标准 ​

判断一条链路更偏主链路还是外围链路,可以先问三件事:

  • 玩家是否必须立刻知道结果
  • 当前运行时状态是否要立即收敛
  • 下游失败是否允许不阻断当前主流程

前两项越强,越偏主链路;后一项越强,越适合事件化外围处理。

常见误区 ​

最常见的错误,是把消息驱动当成架构先进性的象征,连主链路也想统一改造成异步系统。第二类错误,是没有定义清楚“主事实何时成立”,导致主链路和外围链路在失败时一起悬空。第三类错误,是外围系统虽然名义上异步,实际上却仍然决定主流程是否成功,最终既没解耦,也没降低风险。

实战案例:一次「打完了不出结算」的外围反卡主链路排查 ​

案例为教学示例(非摘自真实项目),数字经过取整简化。它把本页「外围系统反向卡住主流程」「一个更稳妥的拆法」「一个简单判断标准」「常见误区」用到深水区:主链路最怕的不是耦合,而是失去确定性——这是结算链路的悬空排查,也是「外围名义上异步,实际上决定主流程是否成功」的完整展开。

背景与现象:一款竞技项目,结算链路原本 P99 320 毫秒。新版本上线「结算即推战报」:结算事务里串行加了两步——同步刷新排行、同步发送战报通知。上线 3 天后的晚高峰(叠加运营推送活动),结算 P99 飙到 4.8 秒——涨到约 15 倍,超时率从 0.2% 升到 6.1%;客服收到「打完了不出结算」工单 1700 单。战斗本身毫无异常,慢的是「打完之后」那一段。

第一步:现象与口径——先把「不出结算」变成可拆的时延账 ​

口径统一:结算时延 = 战斗终局判定 → 结算确认返回客户端。三笔账分开记:

  • 战斗侧:终局判定 P99 80 毫秒,与版本前持平——战斗没变慢
  • 结算侧:结算确认 P99 4.8 秒——悬空点在这里
  • 存储侧:结算事务提交 P95 35 毫秒、无锁等待——数据库没拖

战斗快、事务快、唯独「确认」慢——悬空点不在计算也不在存储,在结算事务之外的同步外部调用上。

第二步:分层归因——从战斗到外部调用的悬空账本 ​

text
战斗层   终局判定 P99 80ms、判罚重算 0 起——排除战斗逻辑
存储层   结算事务提交 P95 35ms、无锁等待——排除数据库
外部账   结算确认里串行两次外部同步调用:
         排行刷新 P99 1.2s + 战报通知 P99 3.4s,通知服务被运营推送打满、重试堆积
时间账   80ms + 35ms + 1.2s + 3.4s ≈ 4.7s ≈ 结算 P99 4.8s——账能对上

账本收束:两次外部同步调用合计 4.6 秒,占结算时延约 96%——主流程在替外围排队。

第三步:根因——外围被做成了主流程成功的前置条件 ​

对照本页「最容易做坏的几个点」,这是「外围系统反向卡住主流程」的标准形态:排行、通知这些本该异步处理的东西,被设计成了结算成功的前置条件。用「一个简单判断标准」的三问核对战报推送:玩家是否必须立刻知道结果(不必,晚几分钟无感);运行时状态是否要立即收敛(不必,战报不影响对局);下游失败是否允许不阻断主流程(必须允许,通知服务不该绑架结算)——三问全指向该异步化,却全被同步串行。「主事实何时成立」也没有定义过:结算事务一提交,胜负与奖励其实已成立,排行与战报只是围绕主事实的后续处理——误区第三类「外围名义上异步,实际上决定主流程是否成功」逐字应验。

第四步:处置与回填——主事实落稳即返回,外围转事件自愈 ​

修法:结算事务提交(胜负落库 + 奖励入账)即返回客户端——主事实落稳;排行刷新、战报通知改为订阅「结算成功」事件,各自消费、重试、补偿,通知失败进补偿队列,不回滚结算;排行延迟改为分钟级可见(产品确认可接受)。复核:结算 P99 4.8 秒 → 350 毫秒,超时率 6.1% → 0.3%,1700 单工单逐单闭环(悬空对局全部补发结算与战报)。

回填清单:

落点动作回填处
主事实口径「主事实何时成立」写进链路文档:事务提交即成立,外围一律不前置一个更稳妥的拆法
前置依赖审计新功能进结算链路必答三问,禁止同步调用排行/通知类外围外围系统反向卡住主流程
事件化改造主事实事件广播 + 外围自订阅,失败重试与补偿不回滚主事务外围链路为什么反而常常适合事件化
悬空监控「请求已返回但结果未落」的悬空率进大盘,主链路看板与战斗侧分离为什么主链路不能简单照搬 MQ 思维

案例的三个教训 ​

  1. 主链路的敌人不是耦合,是失去确定性——同步串入外围,等于把最不稳的下游装进最怕抖的链路。
  2. 「主事实何时成立」要先于架构讨论——事实没定义清楚,失败时主链路和外围一起悬空。
  3. 判断标准三问是新功能进链路的门票——玩家不必立刻知道的事,就不该让打完的人等它。

Used By ​

结算、支付、奖励发放链路评审;「打完了不出结算」一类事故的归因(见页内实战案例)。

上游:IPC 边界(01)与通信模式(02)。下游:幂等与顺序(05)、外部依赖治理(server 树稳定性)。关系链:主/外围判别 → 事件化边界 → 反向卡死防护。

Reference ​

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

游戏知识体系