Q7: 进程内架构 vs 多进程架构,各有什么优劣?
核心结论
这个问题本质上是在讨论“系统边界放在进程内,还是放在进程外”。
- 进程内架构的优势是快、简单、共享状态方便
- 多进程架构的优势是隔离、扩展、容错更好
- 真正合理的选择通常不是绝对二选一,而是先建立清晰边界,再决定这些边界是否需要独立进程
很多项目的演进路径是:
单进程多线程 -> 单机多进程 -> 多机多进程
一、先把边界分清楚
这个问题很容易和另外两个问题混淆:
q6更偏“单机/分布式”的部署级选择q71更偏“线程 vs 进程”的并发执行模型q7关注的是“业务模块是否应该拆成独立进程”
因此,q7 讨论的重点不是“线程怎么调度”,也不是“机器怎么部署”,而是:
- 网络层、逻辑层、数据层是否在同一进程
- 关键模块之间是函数调用,还是 IPC / RPC
- 进程边界带来哪些收益和代价
二、三种常见形态
2.1 单进程多线程
所有模块都在同一个进程里,只是用多个线程并发执行。
┌────────────────────────────────────────────┐
│ 单一进程 │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │网络线程│ │逻辑线程│ │DB线程 │ │
│ └────────┘ └────────┘ └────────┘ │
│ 共享内存 │
└────────────────────────────────────────────┘
特点:
- 模块之间共享内存
- 数据传递成本最低
- 状态管理最直接
- 线程安全问题最集中
2.2 单机多进程
模块被拆成多个进程,但仍然跑在同一台机器上。
┌────────────────────────────────────────────┐
│ 单机部署 │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │Gateway │ │Logic │ │DBProxy │ │
│ │进程 │ │进程 │ │进程 │ │
│ └────────┘ └────────┘ └────────┘ │
│ IPC / 本机 TCP / Unix Socket │
└────────────────────────────────────────────┘
特点:
- 已经有进程隔离
- 已经建立模块边界
- 可以先承受较低的分布式复杂度
- 仍然共享同一台机器的故障域
2.3 多机多进程
模块拆成独立进程后,再按职责或负载分布到多台机器。
Client
│
▼
Gateway 进程 ──► Session / Base 进程 ──► Space / Cell 进程
│
└────────► DB / Cache / MQ
特点:
- 进程边界和部署边界都被拉开
- 扩展能力最强
- 运维、监控、故障处理复杂度最高
三、进程内架构的优势和代价
3.1 优势
1. 通信成本低
同进程内模块协作通常是:
- 函数调用
- 指针引用
- 无需序列化的数据访问
这意味着:
- 延迟更低
- CPU 开销更小
- 数据结构更容易复用
2. 开发效率高
单进程架构对小团队很友好:
- 启动简单
- 调试链路短
- 日志和调用栈更集中
- 问题定位通常更直接
3. 状态管理简单
很多游戏逻辑本身就是强耦合状态机。
如果都在一个进程里:
- 状态共享更自然
- 一致性边界更容易控制
- 不需要先考虑跨进程幂等、重试、补偿
3.2 代价
1. 故障影响面大
只要关键逻辑把进程打崩,往往就是全服务一起倒。
即使不是崩溃,只要出现:
- 死锁
- 长时间卡顿
- 内存破坏
- 主线程被阻塞
影响面也常常是全局的。
2. 模块相互拖累
如果网络、逻辑、存储、AI、后台任务都在一个进程里,很容易出现:
- 一个热点模块拖慢整个服务
- 一个长尾操作堵住主循环
- 一个内存泄漏拖垮全局
3. 扩展边界不清
当系统写成一个巨大进程后,后续要拆分会很痛苦,因为:
- 模块之间共享了太多隐式状态
- 依赖链太长
- 很多调用默认依赖同步语义
这时不是“把进程拆开”这么简单,而是要重建模块边界。
四、多进程架构的优势和代价
4.1 优势
1. 故障隔离更强
多进程最直接的收益就是隔离。
例如:
- 聊天进程异常,不应拖垮战斗
- 某个地图进程异常,不应影响登录
- 某个后台任务堆积,不应阻塞主逻辑
这类隔离在 MMO 里尤其重要,因为服务通常是长时间在线运行的。
2. 更容易按职责扩容
拆进程之后,才能更自然地按模块扩容:
- 网关连接压力高,就扩 Gateway
- 场景模拟压力高,就扩 Space / Cell
- 数据访问压力高,就扩 DBProxy 或缓存层
这也是像 KBEngine 这类架构会把 LoginApp / BaseApp / CellApp / DBMgr 拆开的核心原因。
3. 更容易做资源隔离
不同进程可以单独控制:
- CPU 亲和性
- 内存上限
- 重启策略
- 部署位置
这让性能治理和故障治理更可控。
4. 演进到分布式更自然
只要边界已经是进程级,后续从“本机进程通信”迁移到“跨机进程通信”,工作量通常远小于从单体大进程直接拆分。
4.2 代价
1. 通信成本显著增加
一旦跨进程,原本的函数调用会变成:
- 序列化
- 发送
- 接收
- 反序列化
- 超时处理
- 错误处理
不仅变慢,还会引入新的复杂度。
2. 一致性更难
在同一进程里修改两个模块状态,很多时候只是两次内存写。
在多进程里,则需要考虑:
- 消息是否到达
- 消息是否重复
- 顺序是否被打乱
- 部分成功后如何补偿
这会把很多“本来很普通的业务逻辑”升级成工程问题。
3. 调试和观测更难
跨进程之后,单靠本地断点通常不够,还需要:
- 全链路日志
- Trace ID
- 队列长度监控
- RPC / 消息耗时监控
否则问题会变成“每个进程看起来都没问题,但整体就是卡”。
4. 部署运维复杂度上升
多进程架构会自然引入:
- 进程管理
- 配置管理
- 启停顺序
- 服务发现
- 健康检查
- 自动拉起
这部分成本不能忽略。
五、一个更准确的对比表
| 维度 | 进程内架构 | 多进程架构 |
|---|---|---|
| 模块通信 | 函数调用 / 共享内存 | IPC / RPC / 消息 |
| 状态共享 | 简单 | 需要显式同步 |
| 开发门槛 | 低 | 中高 |
| 调试成本 | 低 | 高 |
| 故障隔离 | 弱 | 强 |
| 资源隔离 | 弱 | 强 |
| 扩容能力 | 弱 | 强 |
| 一致性处理 | 相对简单 | 更复杂 |
| 运维复杂度 | 低 | 高 |
| 适合阶段 | 原型、早期、小规模 | 中后期、复杂业务、长期运营 |
需要注意:
- “多进程”不必然等于“跨机器”
- “进程内”也不等于“不能多线程”
- 真正的区别在于模块边界是否被提升到了进程级
六、MMO 里哪些模块适合拆进程
不是所有模块都值得独立进程。
6.1 适合拆进程的模块
1. 网关 / 接入层
原因:
- 连接数高
- 与业务逻辑边界清楚
- 易于做限流、鉴权、会话转发
2. 空间模拟 / 地图逻辑
原因:
- 是 MMO 最核心的热点来源
- AOI、移动、战斗、可见性广播天然聚集
- 适合按地图、分区、Cell 拆分
3. 数据代理 / 持久化层
原因:
- IO 特征和逻辑特征差异大
- 适合独立限流、连接池、重试和异步写回
4. 后台异步模块
例如:
- 邮件
- 排行榜
- 统计
- 日志归档
- GM 和运营系统
原因是这些模块不应该反向拖累主游戏循环。
6.2 不要轻易拆进程的模块
强耦合且高频交互的逻辑,如果拆开后每帧都要跨进程通讯,通常得不偿失。
典型例子:
- 同一战斗域里的战斗判定
- 同一视野域里的 AOI 更新
- 高频共享的局部状态机
这里的原则是:
高频、强耦合、低粒度交互的逻辑,优先放在同一个进程边界内。
七、KBEngine 为什么偏好多进程
像 KBEngine 这类 MMO 架构,之所以天然偏好多进程,不是因为“多进程更高级”,而是因为 MMO 的核心问题本来就需要隔离和拆分:
- 登录和接入是连接密集型
- 玩家会话和持久状态是会话型
- 空间逻辑是实时模拟型
- 数据读写是 IO 密集型
这些负载特征差异很大,用一个进程全兜住,后期通常会越来越难治理。
把 BaseApp / CellApp 分开,本质上也是在做状态边界划分:
BaseApp更偏玩家持久态、会话态、后台态CellApp更偏空间实时态、AOI、移动、战斗
这不是唯一正确做法,但对 MMO 来说是很自然的一种拆法。
八、如何做选择
8.1 适合优先用进程内架构的情况
- 项目还在原型阶段
- 团队规模较小
- 玩法还未稳定
- 在线规模和空间复杂度都不高
- 先把业务链路跑通比扩展能力更重要
这时最重要的不是“未来能扩多大”,而是:
- 开发效率高
- 问题定位快
- 压测和迭代足够快
8.2 适合认真引入多进程的情况
- 模块之间负载特征差异明显
- 某些模块故障不能拖垮全局
- 某些模块需要独立扩容
- 已经出现明显热点和瓶颈
- 需要长期稳定运营
这时多进程的收益开始大于复杂度成本。
九、一个更务实的演进路线
阶段 1:单进程多线程
适合早期验证。
重点不是“把所有事情做到极致”,而是:
- 模块职责先划清
- 不要滥用全局共享状态
- 提前准备可观测性
阶段 2:单机多进程
这是最容易被忽略,但非常关键的阶段。
建议优先拆:
- 网关
- 场景逻辑
- 数据代理
- 后台异步系统
这个阶段的价值在于:
- 建立进程边界
- 验证协议设计
- 练习异常和超时处理
阶段 3:多机多进程
只有在边界稳定、瓶颈明确、运维能力跟上后,再推进多机部署,收益才会比较确定。
十、总结
进程内架构和多进程架构的核心差异,不是“能不能并发”,而是“模块边界被放在了哪里”。
如果当前目标是快速验证、快速迭代、快速定位问题,进程内架构通常更合适。 如果当前目标是故障隔离、独立扩容、长期稳定运营,多进程架构通常更合适。
更稳妥的工程做法往往不是一开始就全面拆进程,而是先把边界设计对,再根据真实瓶颈决定哪些边界值得升级为独立进程。
