主题
ServerSync(跨服同步 / RPC)
Behemiron Engine 内置的强大的横向分布式同步层,用于
Spigot↔Velocity↔Spigot/Forge的跨服数据同步、玩家传输和 RPC 通信。
Quick Start
kotlin
// 读写玩家域数据
val data = ServerSync.of(player)
data.set("coins", 100)
val coins = data.get<Int>("coins")
// 全局域数据
val guild = ServerSync.global("guild")
guild.set("member_count", 42)
// 跨服 RPC
ServerSync.rpc().query("lobby", GetOnlineCount()) { count ->
println("大厅在线: $count")
}API Reference
入口 API(Spigot)
| 方法 | 返回值 | 说明 |
|---|---|---|
ServerSync.of(player) | 玩家域数据容器 | 按 domain + playerUuid 管理,支持跨服迁移 |
ServerSync.global(domain) | 全局域数据容器 | 多服共享聚合数据,最终一致 |
ServerSync.rpc() | RPC 客户端 | 跨服 RPC 通信 |
ServerSync.transfer() | TransferBuilder | 跨服传输构建器 |
ServerSync.cluster() | 集群查询 | 查询集群状态 |
RPC 语义
| 语义 | 是否需要响应 | 幂等要求 | 说明 |
|---|---|---|---|
query | 是 | 否 | 查询操作,含超时 |
command | 是 | 必须幂等 | 命令操作,重试场景下不可产生重复副作用 |
event | 否 | 否 | fire-and-forget,无需等待响应 |
核心能力
玩家域数据同步
按 domain + playerUuid 管理,支持跨服迁移时自动携带数据。
kotlin
val data = ServerSync.of(player)
// 写入数据
data.set("level", 10)
data.set("guild_id", "abc-123")
// 读取数据
val level = data.get<Int>("level") ?: 1
val guildId = data.get<String>("guild_id")
// 玩家跨服时,数据自动随玩家迁移全局域数据同步
global(domain) 多服共享聚合数据,最终一致。
kotlin
val guild = ServerSync.global("guild")
// 写入(CAS + 审计 + Outbox 落库)
guild.set("member_count", 42)
guild.set("guild_name", "Dragon Slayers")
// 读取(从本地缓存读取,由 Reconciler 保证最终一致)
val count = guild.get<Int>("member_count")跨服 RPC
支持 query、command、event 三种语义。
kotlin
val rpc = ServerSync.rpc()
// Query:查询操作
rpc.query("lobby", GetOnlineCount()) { count ->
println("大厅在线人数: $count")
}
// Command:命令操作(处理器必须幂等)
rpc.command("game-server", KickPlayer(uuid)) { result ->
println("踢出结果: $result")
}
// Event:fire-and-forget
rpc.event("all", ServerShutdownNotice(reason = "维护"))跨服传输
统一 Builder API 支持本区与跨区传输。
kotlin
ServerSync.transfer()
.player(player)
.target("survival-1") // 目标服务器
.fallback("lobby") // 回退服务器
.onSuccess { println("传输成功") }
.onFailure { println("传输失败: $it") }
.execute()主链路
本区 Transfer
TransferBuilder -> SyncOrchestrator -> TransferLedger -> Velocity RPC
- Builder 完成目标服解析/回退策略(排队、失败、回原组)。
- Orchestrator 负责锁、存档、发布、ACK 与恢复。
- Ledger 记录
PREPARE/SAVE/PUBLISH/APPLY/ACK步骤,保证可审计与恢复。
跨区 Transfer
Spigot -> Velocity -> Federation -> (Transfer API / Forge redirect)
- Velocity 端
CrossRegionTransferProcessor负责跨区路由。 - Forge 客户端由
CrossRegionClientTransferHandler收包后发起重连,并回传 ACK。
GlobalData 同步
CAS write -> Outbox -> Dispatcher -> RTopic -> Consumer -> Reconciler
- 写入侧:CAS + 审计 + Outbox 落库。
- 分发侧:
OutboxDispatcher轮询并投递 Redis Topic(至少一次)。 - 消费侧:幂等消费 + 版本对账(Reconciler)收敛最终一致。
RPC 双工
RpcClient -> PendingRequests -> RpcRouter -> RpcProcessor -> response
RpcProcessor采用挂起handleAsync(...)契约。- Query/Command 要求返回响应;Event 为 fire-and-forget。
完整示例
kotlin
@Service
class TransferService {
// 玩家加入时加载跨服数据
fun onPlayerJoin(player: Player) {
val data = ServerSync.of(player)
val lastServer = data.get<String>("last_server") ?: "none"
println("${player.name} 来自 $lastServer")
data.set("last_server", "current-server")
}
// 发起跨服传输
fun transferToLobby(player: Player) {
ServerSync.transfer()
.player(player)
.target("lobby")
.fallback("lobby-backup")
.onSuccess { println("传输成功") }
.onFailure { e -> println("传输失败: $e") }
.execute()
}
// 查询集群状态并选择最优服务器
fun findBestServer(player: Player) {
ServerSync.rpc().query("all", GetServerLoad()) { loads ->
val best = loads.minByOrNull { it.load }
if (best != null) {
ServerSync.transfer()
.player(player)
.target(best.serverId)
.execute()
}
}
}
}注意事项
- Command RPC 必须幂等:重试场景下不可产生重复副作用。
- 领域拆分优先:按业务域拆表,避免单域膨胀。
- 统一 region 前缀:DB 表名 / Redis key / channel 必须同源命名。
- 监控死信与重试:Outbox 堆积优先告警和回放。