Skip to content

ServerSync(跨服同步 / RPC)

Behemiron Engine 内置的强大的横向分布式同步层,用于 Spigot↔Velocity↔Spigot/Forge 的跨服数据同步、玩家传输和 RPC 通信。

前置知识

需要了解 网络系统 的基本概念。纵向(Server↔Client)对象同步请查看 CPDC

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必须幂等命令操作,重试场景下不可产生重复副作用
eventfire-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

支持 querycommandevent 三种语义。

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 堆积优先告警和回放。