Skip to content

IPC 基础与场景边界 ​

Category ​

networking / IPC。节点定位:系统拆开之后,跨边界传递状态与动作的语义边界。

Definition ​

IPC 和消息系统真正解决的,不是“进程之间怎么通信”这种抽象问题,而是系统拆开之后,状态和动作如何跨边界传递。进程、线程、服务一旦被拆开,原来进程内一次函数调用就能完成的事情,都会变成下面这些更现实的问题:

  • 调用方要不要同步等结果
  • 消息重复到达怎么办
  • 顺序错了还能不能恢复
  • 下游处理不过来时,上游是阻塞、丢弃还是积压
  • 一条链路失败后,谁来补偿和重试

所以讨论 IPC,不能从“有没有消息队列”开始,而要从“这条边界上到底在传什么语义”开始。

为什么系统拆开以后必须认真面对 IPC ​

单进程系统里,模块之间通信大多是进程内调用,失败语义相对简单,状态可见性也更直接。系统一旦走到多进程或多服务,边界上的问题会立刻变多:

  • 调用不再免费,序列化、拷贝、调度和网络延迟都会出现
  • 失败从“抛异常”变成“超时、重复、乱序、半成功”
  • 排障从读本地堆栈变成追跨进程链路
  • 版本演进开始涉及协议兼容和灰度窗口

也就是说,IPC 不是简单换了一种 API,而是让模块边界第一次真正暴露出成本。

Problem ​

哪些问题天然适合消息化 ​

消息化最有价值的场景,通常具备下面这些特征:

  • 生产方不必同步等待结果
  • 允许消费者按自己的节奏处理
  • 链路更关注削峰和解耦,而不是最低延迟
  • 同一个事件需要被多个下游复用
  • 下游失败不应该直接拖垮上游主流程

典型例子包括:

  • 日志采集和埋点上报
  • 战斗结束后的战绩、排行、报表更新
  • 邮件、推送、补偿、通知
  • 运营活动的外围联动
  • 跨进程的后台任务分发

这类链路本质上更像“把事实发布出去,再让下游各自处理”。

哪些问题不该轻易消息化 ​

如果一条链路同时要求下面这些特征,就要非常谨慎:

  • 强实时
  • 强顺序
  • 强交互反馈
  • 上下文高度耦合
  • 出错后难以补偿

例如:

  • 局内战斗输入处理
  • 每帧对象同步
  • 场景控制权实时切换
  • 玩家点击后必须立刻得出明确结果的关键交互

这些链路如果被粗暴地改造成消息队列驱动,系统往往会失去可解释性。延迟从毫秒级尾部抖动变成积压不确定性,错误也从“立即失败”变成“过几秒才知道某个消费者没处理好”。

IPC 不是只有 MQ 一种形态 ​

很多团队谈 IPC 就只想到消息队列,这是典型误区。实际工程里常见形态至少包括:

  • 进程内事件总线
  • 共享内存、管道、socket 等本地 IPC
  • RPC 或 gRPC 这类同步调用
  • RPC 配合后台任务的混合模式
  • 基于 Broker 的异步消息系统

它们解决的是不同问题。是否引入 Broker,取决于你要的到底是低延迟调用、跨边界解耦、广播能力,还是削峰与缓冲能力。

判断场景边界时,先看这四件事 ​

1. 调用方是否必须立刻知道结果 ​

如果必须立刻知道结果,这条链路通常更偏同步调用;如果只需要把事件可靠交出去,下游什么时候处理完可以稍后再看,就更适合消息化。

2. 失败代价由谁承担 ​

有些失败必须直接反馈给玩家,例如购买失败、进入副本失败;有些失败可以进入补偿或重试,例如排行刷新、报表更新、推送下发。失败代价不同,通信方式通常也不同。

3. 顺序和幂等要求有多强 ​

如果消息重复一次、乱序一次就会直接破坏状态,说明这条边界上还需要更强的控制,不适合轻率异步化。

4. 链路是单消费还是多消费 ​

如果一个事件天然要被多个系统消费,消息化会明显更自然;如果只是一个调用方等待一个处理结果,硬上 MQ 往往只是在引入额外复杂度。

一个更贴近工程现实的边界判断 ​

可以把 IPC 链路粗分成两类:

  • 命令型链路:更像“请你现在帮我做这件事,并返回明确结果”
  • 事件型链路:更像“这件事已经发生了,你们各自决定怎么处理”

很多通信设计做坏,就是因为命令和事件没分清。把命令伪装成事件,调用方会失去确定性;把事件伪装成命令,系统又会背上不必要的同步耦合。

常见误区 ​

最常见的错误,是一谈跨进程通信就直接上 MQ,把消息系统当成万能总线。第二类错误,是只看中间件能力,不看链路本身的状态语义、失败代价和兼容窗口。第三类错误,是没有区分命令型和事件型边界,结果同步和异步语义混在一起,系统越来越难解释。

实战案例:一次「进副本点了没反应,十秒后才报失败」的场景边界误判排查 ​

案例为教学示例(非摘自真实项目),数字经过取整简化。它把本页「判断场景边界时,先看这四件事」「IPC 不是只有 MQ 一种形态」「一个更贴近工程现实的边界判断」用到深水区:命令型链路被当事件发了出去——这是进副本反馈的悬空排查,也是「把命令伪装成事件,调用方会失去确定性」的完整展开。

背景与现象:某 MMO 把「进副本」交互链路从同步 RPC 一刀切成 MQ 异步投递。改造前点击到明确反馈 P99 180 毫秒;改造后玩家平均等 2.6 秒才有回执,失败场景 10 秒后才弹提示;进副本失败工单从日均 30 起涨到 420 起;一次链路问题定位从 10 分钟拖到 2 小时。

第一步:现象与口径——把「没反应」拆成三笔账 ​

反馈口径:玩家点击「进入副本」→ 界面得到明确的成功或失败,才算这次交互完成。三笔账分开记:

  • 反馈账:改造前 P99 180 毫秒,改造后平均 2.6 秒,失败反馈拖到 10 秒。
  • 失败账:失败工单日均 30 起 → 420 起,多数是「点了没反应」之后的重复点击与追问。
  • 排障账:链路从读本地堆栈变成追跨进程链路,一次定位 10 分钟 → 2 小时。

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

text
进副本反馈失控归因  占比
  命令伪装成事件,同步语义被整条异步化    60%
  失败代价错配,该立刻反馈的失败进了补偿队列    25%
  IPC 形态收敛成单一 MQ,排障要跨进程追队列    15%

逐项比对:第一问「调用方是否必须立刻知道结果」的答案是必须,链路却按事件发了出去;第二问「失败代价由谁承担」的答案是直接反馈给玩家,失败却进了补偿队列;第四问的单消费链路被硬上 MQ。三类都在,命令伪装成事件那条占比最大。

第三步:根因——四件事没答完就选了形态 ​

本页原话:把命令伪装成事件,调用方会失去确定性;延迟从毫秒级尾部抖动变成积压不确定性。团队先定「全面消息化」再看链路,等于把「请你现在做这件事并返回明确结果」的命令,伪装成「这件事已经发生了,你们各自决定怎么处理」的事件——确定性当场消失,反馈变成排队。

第四步:处置与回填——把形态选择权还给语义 ​

  1. 链路回退:进副本、购买、进场景确认回同步 RPC,日志、排行、推送类保留消息化,混合形态各归其位。
  2. 四问进评审:新链路立项先答「立刻知道结果、失败代价、顺序幂等、单/多消费」四问,命令型与事件型判别写进链路文档。
  3. 形态清单恢复:进程内总线、本地 IPC、RPC、Broker、后台任务按语义分工,禁止「一谈跨进程通信就直接上 MQ」。

复核账:进副本反馈 P99 2.6 秒 → 170 毫秒;失败工单 420 起/日 → 25 起/日;链路定位 2 小时 → 8 分钟。

回填清单:

回填项动作回填处
场景边界四问立刻知道结果、失败代价、顺序幂等、单/多消费四问进立项评审判断场景边界时,先看这四件事
命令事件判别命令型走同步或同步入口加异步后处理,事件型才进消息系统一个更贴近工程现实的边界判断
形态选择清单进程内总线、RPC、Broker、后台任务按语义分工,不默认上 MQIPC 不是只有 MQ 一种形态
不消息化清单强实时、强顺序、强交互的链路禁入消息化改造哪些问题不该轻易消息化

案例的三个教训 ​

  1. 「能不能立刻拿到结果」先于「用什么中间件」——四件事没答完就选形态,等于倒着做设计。
  2. 失败代价决定反馈通道——必须直接反馈给玩家的失败放进补偿队列,问题只是被推迟暴露。
  3. 形态单一化是排障成本——五种 IPC 形态收敛成一种,排障从读本地堆栈变成追跨进程链路。

Used By ​

服务拆分评审(哪些调用允许异步化);结算链路设计(外围事件化与主链路同步的分界)。

上游:服务拆分(server 树)。下游:通信模式(02)、主/外围链路(03)、幂等与顺序(05)。关系链:拆分边界 → IPC 语义 → 通信模式 → 幂等与顺序。

Reference ​

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

游戏知识体系