Skip to content

集群运行时语义

本文档包含 Shield 跨进程/跨机器通信(cluster)和旧代码处理相关的运行时语义决策。

shield_cluster 是官方可选模块,不属于 shield_core,也不是最小运行路径。本文用于提前冻结多节点语义,保证未来实现不会把服务发现、节点编排或 CAF 远程细节泄漏进用户 API。

当前状态:

  • 本文冻结 shield_cluster optional module 的第一版契约。
  • 本地 shield.query(name) 继续只查询本地 registry。
  • optional module 的横向 owner、配置归属和 disabled 语义见 官方可选模块契约

shield_cluster 定位

shield_cluster 负责跨进程和跨机器通信,不属于 shield_core

边界约束:

  • 复用 ServiceHandleServiceAddresssend/call、timeout 和错误码语义。
  • 不改变本地 ServiceRegistry 的规则。
  • remote name 只作为 cluster route cache,不进入 core registry。
  • 业务 Lua 默认不感知 CAF middleman。
  • 节点发现和负载均衡策略不反向成为 core 依赖。

与 CAF 的关系

CAF 底层已支持远程 Actor 通信(通过 middleman),shield_cluster 在此基础上封装 Shield 服务语义:

能力CAF 提供shield_cluster 补充
远程 Actor 通信封装为 Shield 服务语义
连接管理节点生命周期管理
消息路由服务名路由、路由 cache
节点发现Phase 1 仅支持静态配置
负载均衡不进入 Phase 1
心跳状态online/suspect/offline/removed

Phase 1 实现策略

基于 CAF middleman:

  • 使用 CAF 的远程 Actor 能力
  • 不重新实现网络传输层
  • 在 CAF 之上封装 Shield 服务语义

静态配置:

  • Phase 1 只支持静态 peers。
  • 不集成 Kubernetes、Etcd、Consul、Zookeeper 或广播发现。
  • 不提供透明负载均衡、自动 sharding 或 leader election。

Public Surface

shield_cluster 只定义两类 public surface:

lua
local h, err = shield.cluster.query("node-2", "room.public")
local nodes = shield.cluster.nodes()

规则:

  • shield.cluster.query(node, name) 负责远端 name 解析。
  • shield.cluster.nodes() 返回当前已知节点及状态摘要。
  • 普通业务消息仍走统一 shield.send/call,不定义 shield.cluster.send/call
  • 节点 connect/disconnect、peer 管理属于配置和运维职责,不作为业务 Lua API 暴露。
  • Phase 1 只支持显式 (node_id, service_name) 寻址。

Cluster 语义

共同约束:

  • NodeId 在同一部署内必须唯一。
  • handshake 必须携带 node_idnode_epoch
  • 重复 NodeId 必须拒绝连接。
  • heartbeat 驱动 online/suspect/offline/removed
  • remote service handle 带 {node, epoch, service_id}
  • remote name 是 cache,不进入 shield_core 的本地 registry。

节点发现

节点发现属于 shield_cluster,不属于 shield_core。Phase 1 只实现静态配置:

方案适用场景外部依赖配置方式
静态配置开发/测试/小型部署cluster.peers
广播发现Phase 2+cluster.discovery: broadcast
RedisPhase 2+Rediscluster.discovery: redis
KubernetesPhase 2+K8s APIcluster.discovery: kubernetes
Etcd/ConsulPhase 2+Etcd/Consulcluster.discovery: etcd

默认实现:静态配置

零依赖,适合开发、测试和小型部署(< 10 节点):

yaml
cluster:
  node_id: "node-1"
  listen: "0.0.0.0:9000"
  peers:
    - "node-2:9000"
    - "node-3:9000"

广播发现(Phase 2+)

局域网自动发现,无需配置 peers 列表:

yaml
cluster:
  node_id: "node-1"
  listen: "0.0.0.0:9000"
  discovery:
    type: broadcast
    broadcast_port: 9001    # 广播端口
    interval: 5000          # 广播间隔(ms)

工作原理:

  • 节点启动后定期广播自己的地址
  • 收到广播的节点自动建立连接
  • 节点离开时通过心跳超时检测

Redis 服务发现(Phase 2+)

基于 Redis 的服务发现,适合小型游戏和已有 Redis 的项目。成本低、实现简单、可靠性足够。

yaml
cluster:
  node_id: "node-1"
  listen: "0.0.0.0:9000"
  discovery:
    type: redis
    redis:
      host: "localhost"
      port: 6379
      password: ""
      db: 0
      prefix: "shield:nodes"    # Redis key 前缀
      ttl: 10                   # 节点注册 TTL(秒)
      heartbeat_interval: 3000  # 心跳间隔(ms)

工作原理:

┌─────────────────────────────────────────────────────────┐
│  1. 节点启动                                             │
│     - 连接 Redis                                        │
│     - 注册节点信息到 Redis                                │
│     - 设置 TTL(默认 10 秒)                              │
├─────────────────────────────────────────────────────────┤
│  2. 心跳续期                                             │
│     - 每 3 秒续期一次                                    │
│     - 更新节点时间戳                                     │
├─────────────────────────────────────────────────────────┤
│  3. 发现其他节点                                         │
│     - 定期扫描 Redis 中的节点列表                         │
│     - 建立连接                                           │
├─────────────────────────────────────────────────────────┤
│  4. 节点下线                                             │
│     - 正常关闭:主动删除 Redis key                        │
│     - 异常崩溃:TTL 过期自动删除                           │
└─────────────────────────────────────────────────────────┘

Redis 数据结构:

# 节点注册(Hash)
HSET shield:nodes:node-1
  addr "192.168.1.100:9000"
  status "online"
  started_at "1234567890"
  last_heartbeat "1234567893"

# 节点列表(Set)
SADD shield:nodes "node-1" "node-2" "node-3"

# 节点计数
GET shield:nodes:count  # "3"

优点:

  • 大多数项目已有 Redis,无需额外部署
  • 实现简单,可靠性足够
  • 成本低,运维简单
  • 支持多机房部署(Redis 复制)

适用场景:

  • 小型游戏(< 50 节点)
  • 已有 Redis 基础设施
  • 不想引入 Etcd/Consul 等重型中间件

外部服务发现(Phase 2+)

大型部署可选集成外部系统:

yaml
# Kubernetes
cluster:
  node_id: "node-1"
  listen: "0.0.0.0:9000"
  discovery:
    type: kubernetes
    namespace: "game"
    service_name: "shield-cluster"

# Etcd
cluster:
  node_id: "node-1"
  listen: "0.0.0.0:9000"
  discovery:
    type: etcd
    endpoints:
      - "http://localhost:2379"
    prefix: "/shield/nodes"

负载均衡

负载均衡不进入 Phase 1。后续可选策略:

策略说明
round-robin轮询
least-connections最少连接数
consistent-hash一致性哈希(按 key)
weighted加权轮询

Phase 1 中,负载均衡由业务层或 pool service 自行实现,不进入 core 或 shield_cluster public API。

单机多进程模式

对于需要多进程但不需要分布式的场景,支持单机多进程部署:

yaml
# node-1.yaml
cluster:
  node_id: "node-1"
  listen: "127.0.0.1:9000"
  peers:
    - "127.0.0.1:9001"

# node-2.yaml
cluster:
  node_id: "node-2"
  listen: "127.0.0.1:9001"
  peers:
    - "127.0.0.1:9000"

启动命令:

bash
./shield --config node-1.yaml &
./shield --config node-2.yaml &

适用场景:

  • 利用多核 CPU
  • 故障隔离(一个进程崩溃不影响其他)
  • 热更新(逐个进程重启)

Phase 1 实现范围

shield_cluster 第一版实现范围:

  • 静态 peers 配置。
  • 节点连接管理(基于 CAF middleman)。
  • 节点心跳和状态管理。
  • 远端路由 cache。
  • 显式 (node_id, service_name) 查询。
  • send/call 跨节点错误语义。

Phase 1 不做:

  • 动态服务发现。
  • 透明负载均衡。
  • 全局唯一服务名竞争。
  • 分布式一致性、leader election、自动 sharding。
  • 玩家迁移、全局锁、排行榜、跨节点配置推送。

后续扩展:

  • Redis discovery。
  • Kubernetes 集成
  • Etcd/Consul 集成
  • 高级路由策略

旧代码处理策略

旧模块处理顺序:

  1. 标记所有 public header 中的 CAF 泄漏点。
  2. 建立 forbidden include 检查。
  3. 抽出 shield_base 基础类型。
  4. 重建 ServiceHandle / ServiceRegistry
  5. 替换旧 service_api 中 CAF 直出 API。
  6. 把 discovery/metrics/health/plugin/DI 等旧模块移出 core 路径。
  7. 保留有价值代码时必须归入明确 target。
  8. 无 target 归属的旧代码删除或移入实验区。

当前已知需要重点清理:

txt
include/shield/service/service_api.hpp
include/shield/service/service_handle.hpp
src/service/service_api.cpp
src/actor/actor_starter.cpp

Apache License 2.0