进程拓扑与职责切分
BigWorld 服务端不是单进程游戏服务器,而是多进程 MMO 运行时。理解它的第一步,是把“玩家会话”“全局实体”“空间模拟”“数据库”“登录入口”“控制面”和“故障恢复”拆开看。
核心结论
BigWorld 的进程拓扑体现了典型 MMO 服务器分层:
LoginApp面向客户端登录入口。BaseApp承载玩家会话、Base 实体、全局逻辑和与客户端的连接关系。CellApp承载空间内实体、AOI、物理/位置、Cell 实体逻辑。DBApp承载数据库交互、实体持久化、账号/实体查询。BaseAppMgr、CellAppMgr、DBAppMgr是控制面和注册中心的一部分。Reviver负责监控关键组件并请求machined拉起恢复进程。
这不是现代微服务式按业务域切分,而是围绕 MMO 的运行时权威域切分。
进程拓扑
LoginApp
处理外部登录请求,完成挑战、认证、分配后续连接目标。
BaseApp
管理玩家在线会话、Base 实体、跨 Cell 协调和客户端通信。
CellApp
负责空间模拟、实体位置、AOI、Ghost、Cell 内逻辑。
DBApp
封装数据库任务,把阻塞数据库操作隔离到后台任务模型中。
Mgr 组件
负责注册、发现、负载信息、扩展接纳、恢复协调和全局控制。
Reviver
通过 ping、birth/death 消息和 machined 交互做进程级恢复。
flowchart LR Client[Client] Login[LoginApp] Base[BaseApp] Cell[CellApp] DB[DBApp] BAM[BaseAppMgr] CAM[CellAppMgr] DBM[DBAppMgr] Rev[Reviver] Machined[machined]Client --> Login Client <--> Base Base <--> Cell Base <--> DB Cell <--> DB Base --> BAM Cell --> CAM DB --> DBM BAM <--> CAM DBM <--> BAM DBM <--> CAM Rev --> Machined Rev -. ping/death .-> BAM Rev -. ping/death .-> CAM
启动与注册链路
概述: BigWorld 服务端进程启动后,需要向 machined 注册,并与 Mgr 组件建立连接。这是进程发现和负载均衡的基础。
源码入口: baseappmgr.cpp:389
// baseappmgr.cpp:389 - BaseAppMgr 注册
bool BaseAppMgr::init( bool isReload )
{
// 1. 注册 Mercury 接口
if (!this->BaseAppMgrInterface::registerWithInterface())
{
ERROR_MSG( "BaseAppMgr::init: Failed to register interface\n" );
return false;
}
// 2. 向 machined 注册
if (!this->registerWithMachined())
{
ERROR_MSG( "BaseAppMgr::init: Failed to register with machined\n" );
return false;
}
// 3. 监听其他组件 birth/death
this->BaseAppMgrInterface::registerBirthListener(
&BaseAppMgr::onBaseAppBirth );
this->BaseAppMgrInterface::registerDeathListener(
&BaseAppMgr::onBaseAppDeath );
return true;
}流程图:
sequenceDiagram participant Process as 服务进程 participant Machined as machined participant Mgr as Mgr 组件 participant Other as 其他进程Process->>Process: 初始化 Process->>Process: registerWithInterface() Process->>Machined: 注册 (birth 消息) Machined->>Mgr: 广播 birth Mgr->>Mgr: addApp() Mgr->>Process: 注册成功 Process->>Other: 监听 birth/death
详细讲解:
接口注册:
registerWithInterface()注册 Mercury 消息处理器,让进程可以接收和处理消息。machined 注册:
registerWithMachined()向 machined 发送 birth 消息,machined 会广播给其他进程。Mgr 接纳:Mgr 组件收到 birth 消息后,调用
addApp()接纳新进程,更新进程列表和负载信息。birth/death 监听:进程监听其他组件的 birth/death 消息,用于服务发现和故障检测。
BaseAppMgr 启动流程
源码入口: baseappmgr.cpp:389
// baseappmgr.cpp:389 - BaseAppMgr 启动
bool BaseAppMgr::init( bool isReload )
{
// ... 其他初始化 ...
// 注册接口
if (!this->BaseAppMgrInterface::registerWithInterface())
{
return false;
}
// 向 machined 注册
if (!this->registerWithMachined())
{
return false;
}
// 监听 BaseApp birth
this->BaseAppMgrInterface::registerBirthListener(
&BaseAppMgr::onBaseAppBirth );
// 监听 CellAppMgr birth
this->CellAppMgrInterface::registerBirthListener(
&BaseAppMgr::onCellAppMgrBirth );
return true;
}关键细节:
- BaseAppMgr 需要知道所有 BaseApp 和 CellAppMgr
- 通过 birth/death 消息维护进程列表
- 负载信息通过定期上报收集
CellAppMgr 启动流程
源码入口: cellappmgr.cpp:238
// cellappmgr.cpp:238 - CellAppMgr 启动
bool CellAppMgr::init( bool isReload )
{
// ... 其他初始化 ...
// 注册接口
if (!this->CellAppMgrInterface::registerWithInterface())
{
return false;
}
// 向 machined 注册
if (!this->registerWithMachined())
{
return false;
}
// 监听 CellApp birth
this->CellAppMgrInterface::registerBirthListener(
&CellAppMgr::onCellAppBirth );
return true;
}关键细节:
- CellAppMgr 管理所有 CellApp,负责空间分配和负载均衡
- 收到 CellApp birth 后,调用
addApp()接纳 - 收到 CellApp death 后,触发实体恢复流程
职责边界
LoginApp
LoginApp 的职责是入口而不是长期会话承载。它注册外部 LoginInterface 和内部 LoginIntInterface,并监听 DBAppMgr 出生消息。源码入口在 loginapp.cpp。
设计取舍:
- 优点:登录入口与游戏会话隔离,便于限流和认证。
- 代价:登录成功后的连接迁移、挑战协议、DB 查询链路更复杂。
BaseApp
BaseApp 是玩家在线会话和 Base 实体的核心进程。它需要知道 BaseAppMgr、CellAppMgr,并处理其他 BaseApp 的 birth 消息。源码入口:
- 注册内部接口:baseapp.cpp
- 注册外部接口:baseapp.cpp
- 处理
CellAppMgrbirth:baseapp.cpp - 处理
BaseAppMgrbirth:baseapp.cpp
CellApp
CellApp 是空间模拟进程。它向 CellAppMgr 注册,由 CellAppMgr 统一做空间、Cell、负载均衡和恢复协调。CellApp 接入 CellAppMgr 的网关请求在 cellappmgr_gateway.cpp。
Manager 组件
Mgr 组件不是业务微服务,而是控制面:
BaseAppMgr管理 BaseApp 集合、负载和 BaseApp 之间的发现。CellAppMgr管理 CellApp、空间、Cell 边界、负载均衡和恢复。DBAppMgr管理 DBApp、数据库组件出生死亡、与 Base/Cell 管理器协调。
CellAppMgr::addApp() 是运行时接纳新 CellApp 的关键入口,见 cellappmgr.cpp。
动态扩展的真实含义
BigWorld 支持运行中接纳新 BaseApp / CellApp,但源码里的扩展入口是进程注册、Mgr 接纳、负载再平衡和状态迁移,不是单纯拉起无状态副本。
更准确的模型是:
这种设计的重点是运行时迁移和控制面协调,而不是只增加进程数量。
源码取舍
BigWorld 的进程拆分服务于明确的运行时职责边界:
- 多进程天然隔离崩溃域,比单进程多线程更容易定位故障。
- C++ 主循环加 Python 脚本嵌入,适合把性能敏感路径留在引擎层。
- Manager 作为权威控制面,避免每个 App 都维护全局复杂状态。
machined和Reviver形成进程级发现、启动和恢复能力。- BaseApp、CellApp、DBApp、LoginApp 的进程角色和内部接口在源码中分别注册,便于按职责排查。
源码代价也很清楚:
- 组件间耦合强,进程拓扑必须和 Mercury 接口、birth/death 通知、Mgr 状态一致。
- 扩容是否有效取决于 Base/Cell 负载迁移,不取决于进程是否已经启动。
- DBApp、BaseAppMgr、CellAppMgr 等控制面异常会影响多个业务路径。
- 排障必须跨 machined、Manager、App 自身日志和 watcher 状态一起看。
源码验证重点
进程拓扑验证应覆盖注册、接纳和状态传播:
- 各进程启动时必须注册对应 Mercury interface。
- App 向 machined 注册的 component 类型、地址、pid 和 watcher nub 信息应正确。
CellAppMgr::addApp()在缺少 BaseApp 或 Alpha DBApp 时不能接纳新 CellApp。- 新 BaseApp/CellApp 接纳后,Mgr watcher 中的数量、负载和 App 列表应同步更新。
- birth/death 通知应能传播到依赖方,不能只在本地进程可见。
- Reviver 或 machined 层面的重启不能绕过 BigWorld 自身的状态恢复流程。
后续研究入口
进程拓扑只是外壳。真正决定性能和扩展边界的是:
- 每个进程内部的事件循环模型,详见 主循环、Tick 与事件分发。
- Mercury 网络栈如何把 socket 事件转成消息,详见 网络 I/O 模型选择。
- EntityDef 如何把脚本类型、协议流和持久化绑定在一起,详见 序列化与 EntityDef。
- CellAppMgr 如何做 Cell 分区、负载均衡和实体迁移,详见 Cell 分区与负载均衡。
- 进程故障恢复机制,详见 动态扩展与容灾。
