c++游戏后端开源框架学习——wukong(三、服务注册与发现)
Nexus:服务注册与发现
对应代码目录:
nexus/,客户端代码在share/agent/
一、Nexus 是什么?
Nexus 是 Wukong 框架的服务注册与发现中心,它替代了传统方案中的 ZooKeeper。
打个比方:Nexus 就像一栋大楼的前台总机——每个入驻的部门(服务器)都要到前台登记,前台知道所有部门的位置。当某个部门需要找其他部门协作时,不用自己去挨个问,前台会主动把相关部门的联系方式推送过来。
为什么不用 ZooKeeper?
| 对比项 | ZooKeeper | Nexus |
|---|---|---|
| 部署方式 | 需独立部署集群 | 框架内嵌进程 |
| 服务注册 | 创建临时节点 | TCP连接+发送注册消息 |
| 服务发现 | 客户端监听节点变化 | 主动推送关注的服务列表 |
| 服务下线 | 临时节点消失 | 断线保护期→超时清理→通知 |
| 关注关系 | 无(客户端自行管理) | 内置关注关系表 |
| 运维成本 | 高(需维护ZK集群) | 低(普通进程) |
核心优势:内置"关注关系"配置,Nexus 知道谁需要知道谁,自动精准推送,不需要业务方自己管理监听逻辑。
二、核心设计
2.1 关注关系表
Nexus 配置文件中定义了服务间的"关注关系"(nexus_config.json.template):
Front(前置转发) 关注 → Gateway(网关)
Login(登录服) 关注 → Gateway(网关)
Gateway(网关) 关注 → Lobby(大厅) + Scene(场景)
Lobby(大厅) 关注 → Gateway(网关) + Record(记录) + Battle(战斗)
Battle(战斗) 关注 → Lobby(大厅)
Nexus 内部维护两张表:
concern_map_:我关注谁(serverType → set<concernServerType>)be_concern_map_:谁关注我(serverType → set<beConcernedServerType>)
这样当某个服务上线/下线时,Nexus 知道该通知哪些服务。
2.2 三种连接状态
Nexus 管理三类连接,用不同数据结构维护:
┌─────────────────────────────────────────────────────────┐
│ Nexus │
│ │
│ 1. 未认证连接(unaccessLink_) │
│ 刚连上但还没发注册消息的连接 │
│ → 10秒超时自动关闭 │
│ │
│ 2. 正常服务对象(serverObjectMap_) │
│ 已认证且连接正常的服务 │
│ → 正常提供服务信息 │
│ │
│ 3. 断线服务对象(disconnectedLink_) │
│ 已认证但连接断了的服务 │
│ → 30秒保护期内可重连恢复 │
│ → 超时后彻底删除并通知关注方 │
│ │
└─────────────────────────────────────────────────────────┘
使用 TimeLink<T>(时间链表)管理超时,O(1) 的入链/删除操作。
2.3 核心类
| 类 | 文件 | 职责 |
|---|---|---|
NexusServer | nexus_server.h | 入口,初始化IO和消息服务器 |
NexusConfig | nexus_config.h | 解析配置,管理关注关系表 |
ServerManager | server_manager.h | 管理服务对象生命周期(单例,非线程安全——单线程事件循环) |
ServerObject | server_object.h | 封装一个已注册服务的状态 |
NexusHandler | nexus_handler.h | 消息处理器(注册/更新/断线) |
三、完整工作流程
3.1 服务注册流程
新服务启动 Nexus
│ │
│── TCP连接 ────────────────────→│ 连接建立,加入"未认证连接"表
│ │
│── S2N_MESSAGE_ID_ACCESS ─────→│ 发送注册消息(含ServerInfo)
│ (server_type, server_id, │
│ rpc_host, rpc_port, ...) │
│ │
│ Nexus 处理:
│ 1. 验证连接是未认证连接
│ 2. 创建ServerObject
│ 3. 移入"正常服务"表
│ 4. 查找该服务关注的服务列表
│ 5. 查找关注该服务的服务列表
│ │
│←── N2S_ACCESS_RSP ────────────│ 返回该服务关注的服务列表
│ (关注的服务信息列表) │
│ │
│ 同时通知关注方:
│←── N2S_SVRINFO ───────────────│ 向关注该服务的服务推送上线通知
│ (新服务信息) │
│ │
│ AgentManager 收到后: │
│ → 调用各Agent.resetStubs() │
│ → 创建RPC Stub │
3.2 服务发现流程
服务发现是被动推送的,不需要主动查询:
- 服务A注册时,Nexus 把A关注的所有服务列表推给A
- 同时,Nexus 把A上线的信息推给关注A的所有服务
- 运行中如果服务B上线/下线/状态变更,Nexus 自动通知关注B的所有服务
3.3 状态更新流程
服务状态变化(如负载信息变更)
│
│── AgentManager.updateRoutine(每10秒)
│ 检测到状态变化 → 上报Nexus
│
│── Nexus.updateHandle()
│ 1. 更新ServerObject信息
│ 2. 通知所有关注方 N2S_SVRINFO
│
└── 关注方收到通知
→ Agent.setStub() 更新本地RPC Stub
3.4 断线与重连流程
连接断开
│
├── 若是未认证连接 → 直接删除
│
└── 若是已认证服务 → moveToDisconnected()
│
│ 进入"断线保护期"(默认30秒)
│ ┌─────────────────────────────────┐
│ │ 保护期内: │
│ │ - 不通知关注方下线 │
│ │ - 保留ServerObject │
│ │ - 等待重连 │
│ └─────────────────────────────────┘
│
├── 30秒内重连 → addConnectedServer()
│ → 恢复到"正常服务"表
│ → 通知关注方更新信息
│
└── 30秒超时 → clearExpiredDisconnectedRoutine()
→ 彻底删除ServerObject
→ 通知关注方 N2S_RMSVR(服务移除)
→ 关注方 Agent.removeStub()
断线保护的意义:网络抖动很常见,如果连接一断就通知下线,重连后又通知上线,会导致大量不必要的状态变更和Stub重建。30秒保护期让短暂抖动对系统透明。
四、客户端侧:AgentManager
AgentManager(share/agent/agent_manager.h)是 Nexus 的客户端,运行在每个业务进程中:
class AgentManager {
// 连接Nexus
void init(IO *io, const std::string &nexusHost, uint16_t nexusPort,
const pb::ServerInfo &serverInfo);
// 启动(在协程中连接Nexus)
void start();
// 注册Agent类型
void registerAgent(int serverType, Agent *agent);
// 更新本服信息(触发状态上报)
void setLocalServerInfo(const pb::ServerInfo &serverInfo);
};
注册的消息处理器:
CORPC_MSG_TYPE_CONNECT→ 连接成功后发送注册消息CORPC_MSG_TYPE_CLOSE→ 断线后自动重连N2S_MESSAGE_ID_ACCESS_RSP→ 初始化所有Agent的Stub列表N2S_MESSAGE_ID_SVRINFO→ 服务上线/变更,调用Agent.setStub()N2S_MESSAGE_ID_RMSVR→ 服务下线,调用Agent.removeStub()
状态上报协程 updateRoutine:每10秒检测本服信息是否变化,变化则上报Nexus。
五、负载均衡
Nexus 提供服务列表,负载均衡由 Agent 实现:
// Agent基类的randomServer方法
ServerId randomServer(ServerId serverId) {
// 从stubInfos_中随机选一个
auto it = stubInfos_.begin();
std::advance(it, rand() % stubInfos_.size());
return it->first;
}
这是最简单的随机负载均衡。框架旧版 ClientCenter 中有更复杂的基于"在线人数"的加权负载均衡,但已被禁用。
六、解决的问题
| 问题 | Nexus 的解决方案 |
|---|---|
| 服务发现需要独立组件 | 内嵌进程,无需额外部署 |
| 服务间不知道该关注谁 | 配置文件定义关注关系,自动推送 |
| 网络抖动导致频繁上下线 | 30秒断线保护期 |
| 未认证连接堆积 | 10秒超时清理 |
| 服务状态变更通知不及时 | 10秒上报周期 + 事件驱动推送 |
| 负载均衡 | 提供服务列表,Agent随机选择 |
面试要点
Q: Nexus 相比 ZooKeeper / etcd 有什么优劣?
优势:
- 轻量级,无需独立部署集群,运维简单
- 内置关注关系,精准推送,减少不必要通知
- 断线保护机制,对网络抖动友好
- 基于框架内通信协议,无额外协议开销
劣势:
- 无持久化(重启后所有服务需重新注册)
- 单点问题(Nexus挂了影响新服务注册,但已有连接不受影响)
- 无强一致性保证(ZooKeeper有ZAB协议保证一致性)
- 功能单一(无配置中心、分布式锁等ZK的通用功能)
Q: 如果 Nexus 挂了怎么办?
已有连接的 RPC 通信不受影响(Agent 已持有 Stub),但:
- 新服务无法注册
- 服务上下线无法感知
- 客户端新登录会受影响(Login需要通过Nexus发现Gateway)
解决方案:Nexus 重启后,各服务的 AgentManager 检测到断线会自动重连重新注册。
更多推荐


所有评论(0)