Nexus:服务注册与发现

对应代码目录:nexus/,客户端代码在 share/agent/

一、Nexus 是什么?

Nexus 是 Wukong 框架的服务注册与发现中心,它替代了传统方案中的 ZooKeeper。

打个比方:Nexus 就像一栋大楼的前台总机——每个入驻的部门(服务器)都要到前台登记,前台知道所有部门的位置。当某个部门需要找其他部门协作时,不用自己去挨个问,前台会主动把相关部门的联系方式推送过来。

为什么不用 ZooKeeper?

对比项ZooKeeperNexus
部署方式需独立部署集群框架内嵌进程
服务注册创建临时节点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 核心类

类文件职责
NexusServernexus_server.h入口,初始化IO和消息服务器
NexusConfignexus_config.h解析配置,管理关注关系表
ServerManagerserver_manager.h管理服务对象生命周期(单例,非线程安全——单线程事件循环)
ServerObjectserver_object.h封装一个已注册服务的状态
NexusHandlernexus_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 服务发现流程

服务发现是被动推送的,不需要主动查询:

  1. 服务A注册时,Nexus 把A关注的所有服务列表推给A
  2. 同时,Nexus 把A上线的信息推给关注A的所有服务
  3. 运行中如果服务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 检测到断线会自动重连重新注册。

更多推荐