房间分布式 Actor 方案
概述
单机 Actor 解决并发,分布式 Actor 解决容量:房间 Actor 分散到多台机器,玩家跨节点协作。Akka Cluster 提供成员管理、Sharding 提供按房间路由与自动迁移、故障恢复保证节点宕机不丢局。本文设计跨节点房间 Actor 方案:Cluster 通信、分片路由、热迁移与故障恢复。
一、Akka Cluster 基础
1.1 集群模型
Akka Cluster:
多个节点组成集群(自动发现/加入)
成员状态机:Joining → Up → Leaving → Exiting
种子节点引导(seed nodes 启动集群)
通信:
Actor 引用跨节点透明(远程消息)
Cluster 提供故障检测(节点心跳)
Gossip 协议同步集群状态集群配置要点:
seed-nodes:启动引导地址
roles:节点角色(网关/逻辑/匹配)
gossip:集群元数据同步
网络:akka remote 基于 TCP/Artery1.2 跨节点消息
// 跨节点发送与本地无差别(位置透明)
ActorRef ref = context.actorSelection(
"akka://game@node2:2552/user/room/room-1001");
ref.tell(new JoinRoomMsg(playerId), self());消息可靠性:
远程消息默认 at-most-once(可能丢)
关键消息用 akka-persistence 或业务幂等兜底
分布式环境需要重试与超时设计二、分片 Sharding 路由
2.1 为什么分片
无分片的问题:
房间 Actor 散落各节点 → 不知道哪个节点
玩家请求要跨节点查找 → 路由表维护复杂
节点增减 → 路由失效
Sharding 解决:
Entity(房间 Actor)按 key 分片
自动路由到 key 所在节点
节点增减自动 rebalance2.2 Sharding 机制
分片原理:
key(如 roomId)→ 哈希 → 分片编号(shard id)
shard → 节点(分片分配)
同一 shard 的 Entity 在同一节点
核心参数:
分片数量:shards(如 100-1000)
每个分片实体数:shard-entity 上限
空闲实体回收:passivate 超时// 分片注册
ClusterSharding.get(system).start(
"RoomShard",
RoomActor::props,
ClusterShardingSettings.create(system),
(msg) -> ((RoomMsg) msg).getRoomId() // 提取分片 key
);
// 发送到分片(自动路由)
ActorRef roomRef = sharding.entityRefFor("RoomShard", roomId);
roomRef.tell(new JoinRoomMsg(playerId), self());2.3 分片 key 选择
key 选择原则:
稳定性:生命周期内不变(roomId 稳定)
均衡性:分布均匀(避免热点 shard)
关联性:相关实体同 key(房间消息都按 roomId)
玩家/房间两种视角:
玩家请求 → 需定位房间 → 用 roomId 分片
玩家私聊 → 用 playerId 分片
不同业务不同分片三、Actor 热迁移
3.1 重平衡(Rebalance)
节点增减时自动迁移:
节点下线 → 其 shard 迁移到其他节点
节点上线 → 部分 shard 分配过来
rebalance 期间 Entity 保持可用(graceful)
迁移过程:
1. 目标节点创建 Entity 并加载状态
2. 切换路由到新节点
3. 旧节点 Entity 停止(passivate)状态迁移要求:
Entity 状态可从持久化恢复(快照/事件)
迁移前 flush 未落盘数据
迁移期间新消息缓冲或重定向3.2 被动化(Passivation)
空闲回收:
房间长时间无消息 → passivate(停 Entity 释放内存)
有消息再来 → 重新激活(从持久化恢复)
参数:
passivate-idle-entity-after:空闲超时
keep-persistent:状态持久化策略游戏应用:
空房间 → 延迟 passivate(保留对战重连窗口)
玩家离线房间 → passivate → 重连时恢复
内存释放 + 恢复成本权衡四、故障恢复
4.1 节点故障
节点宕机处理:
Cluster 检测节点 Down
该节点 shard 重新分配到存活节点
房间 Entity 在新节点重建
重建流程:
1. 新节点创建 RoomActor
2. 从持久化恢复房间状态(快照/事件回放)
3. 通知成员玩家重连
4. 恢复对局(或按规则判负/结算)持久化依赖:
akka-persistence:事件存储(Cassandra/MySQL)
或业务自建快照(Redis/DB)
故障恢复 = 状态可重建的前提4.2 对局恢复策略
房间恢复策略(按玩法):
回合制:恢复断点继续(快照恢复)
实时对战:重建房间 → 玩家重连 → 重新开始
特殊处理:无法恢复时按规则结算(判负/平分)
玩家视角:
断线重连(见帧同步/回合制章节)
重连拿到房间最新快照// 重启恢复示例:RoomActor preStart 加载快照
@Override
public void preStart() {
RoomSnapshot snap = roomStore.load(roomId);
if (snap != null) {
state.restore(snap);
}
}五、实现要点
设计清单:
Cluster 多节点(角色划分:网关/逻辑)
Sharding 按 roomId 分片路由
rebalance 自动迁移(节点增减)
passivate 空闲回收
持久化支撑故障恢复
玩家重连兜底工程注意:
分片数量规划(过多分片 → 元数据开销大)
Entity 状态持久化策略(快照 vs 事件)
远程消息超时与重试
网络分区(脑裂)→ 仲裁策略常见坑:
路由失效(节点切换瞬间)→ 重试 + 幂等
分片热点(热门房间集中)→ 细粒度分片/拆实体
状态未持久化就迁移 → 数据丢失
脑裂双主 → 集群仲裁(split-brain resolver)与其他系统衔接:
Actor 应用 → Actor 模型章节
分片策略 → 房间分片章节
跨服协作 → 跨服通信章节
持久化 → Event Sourcing 章节