← ALL ARTICLES
游戏网络7 MIN READ

对局服务器架构

全库目录221
本文目录13

同一场对局的消息不能随机分给多台服务器。对局架构先确定权威实例,再安排会话路由与持久化边界。实时热状态与交易状态有不同延迟和一致性要求,故障恢复必须保持单一所有者。

会话与服务职责

常见链路是登录服务验证身份,网关维持外部连接并做限流,大厅与匹配组织队伍,对局服务器运行权威对局。库存、社交、排行等服务按业务需要独立。服务间传递稳定 ID,不依赖进程内对象地址。

独立服务器没有本地玩家,权威边界清楚,适合竞技和持久世界。玩家主机服务器由一名玩家同时做主机,成本低但主机有延迟优势,退出会触发主机迁移。P2P 适合少量协作或中继场景,NAT、安全和作弊风险更难处理。

发现、负载与实例

服务发现维护可用实例,负载均衡器按区域、容量和会话粘性分配。实时对局通常先创建实例,再把玩家路由到固定实例;不能把同一场对局的逐帧消息随机发到不同服务器。

一致性哈希在节点变化时减少键迁移,可用于缓存或分片路由,但热点账号和热门区域仍会倾斜。选择分片键前要看访问模式,而不是只追求平均数量。

匹配请求使用稳定分配ID,调度器先原子预留容量并写Allocated,再启动带实例ID、构建版本与内容哈希的进程。进程完成地图加载和依赖检查后回报Ready;只有分配ID仍有效、实例版本匹配且容量未超限时,路由记录才发布给玩家。

Ready采用条件提交:同一事务比较分配代次、Allocated状态、预留席位与实例版本,再把状态改为Ready并写路由。取消分配也比较同一状态和代次;取消先提交时,迟到Ready失败并终止对应进程,Ready先提交时取消走已启动对局的排空流程。这样检查与发布之间没有能把已取消实例重新上线的空隙。

重复匹配请求查询同一分配结果,不能因为启动回执超时再无条件创建第二局。启动失败释放该次预留并记录终止结果;下次重新分配使用新的分配代次。

玩家加入票据绑定账号、对局、席位、有效期和分配代次,由实例核对并以席位ID幂等接纳。网关只是连接入口,对局实例仍检查票据与当前所有权。下线时实例先停止接新局,完成现有对局或进入明确的迁移流程,最后撤销路由并释放容量;直接从发现列表删除不能搬走仍在内存中的对局状态。

进程应支持健康检查、优雅下线和容量上报。健康不只是端口能连,还要包括 Tick 延迟、队列积压和关键依赖状态。

存储与一致性

实时对局服务器保持战斗热状态,持久化服务保存账号、库存和进度。模拟不逐个对象执行数据库查询。是否把每个Tick耐久提交由恢复目标决定;后文采用每Tick共同提交的教学协议,它需要为日志写入安排延迟与吞吐预算。对局结果可先写耐久日志或事件,再异步更新派生榜单。

强一致用于支付、所有权和稀缺物品;允许短时最终一致的社交显示和统计可以异步。事务只覆盖明确边界,跨服务用 Outbox、Saga 或补偿,而不是假设一次远程调用等同数据库事务。

安全与可观测性

网关承担基础 DDoS 防护、连接上限和限流,业务服务继续验证权限。服务间身份、密钥轮换和最小权限不能因为在“内网”就省略。管理接口与玩家流量隔离。

日志、指标和 Trace 使用统一请求/Session/Match ID。实时服务重点看 Tick Time、连接数、丢包、队列、序列化字节、AOI 实体数和跨区失败。告警应对应可行动阈值,并有降级策略,例如停止接受新局而不是拖垮已有对局。

故障与测试

测试不只做正常登录。需要注入服务重启、数据库超时、重复消息、乱序、分区网络和节点扩缩容。确认交易幂等、玩家不会同时存在于两个权威实例、旧会话无法重放操作,并验证监控能定位故障范围。

无缝大世界、全球低延迟和绝对防作弊都不是单一架构开关。设计时先写清玩家规模、交互半径、Tick Rate、持久化要求和可接受故障,再选择分区与一致性策略。

所有权与持久化时序

对局实例拿到 matchId 以后,还要证明自己是当前所有者。权威持久记录保存实例ID、进程代数与递增epoch,玩家命令关联输入会话与对局;持久化事务在写入结果或日志时比较同一记录中的epoch,旧进程即使仍在模拟也不能确认新结果。路由是该记录的镜像,交接按 世界分区与AOI 的冻结、准备、条件提交协议执行。只在网关换路由不能阻止旧实例在网络分区后继续写入结果。

对局结束时先生成稳定结果ID,把胜负与奖励请求写入耐久边界,再通知玩家。通知丢失时查询同一结果ID,奖励服务按结果ID幂等结算。若客户端已看到胜利但写入尚未成功,服务器崩溃会留下无法解释的结果,因此“显示完成”必须对应明确的提交状态。

延迟预算可拆成入口排队、等待下一Tick、模拟、序列化和发送。某实例Tick变慢时,负载均衡优先停止分配新局;已有对局的热状态迁移另有冻结和交接成本,不能像无状态HTTP请求一样即时换机器。

结果提交与实例恢复

可以用一条具体结果链检查架构:match42由实例A、epoch7运行,结束结果ID为r42。A把最终Tick、胜负和奖励事件写入同一耐久提交记录,提交成功才广播已完成。奖励服务以r42去重,并把领取记录和库存更新放在同一事务内。排行榜是派生视图,即使暂时落后,也能重放r42恢复。

若耐久提交后广播前A崩溃,查询r42能返回同一结果,奖励重发也不会重复发放。若提交前崩溃,没有结果记录就不能由客户端胜利动画补造奖励。正在进行的对局能否恢复取决于是否保存模拟快照和后续输入日志:有日志时在新epoch恢复到最后耐久Tick后重放,没有日志时按项目规则终止或重赛,不能声称单靠会话路由恢复热状态。旧epoch实例即使重新连上存储,也会被提交端拒绝。

运行中恢复的状态链

一份可恢复对局从版本固定的初始状态开始,保存地图与规则哈希、随机数状态、对象ID分配器、每个输入会话的消费序号,以及会影响后续模拟的玩法和物理状态。输入日志记录经过验证的输入、稳定顺序和目标Tick;外部服务事件同样记录为确定的Tick输入。依赖墙钟、异步加载完成时刻或不稳定容器顺序的结果,要在日志中记录其权威决策,不能恢复时重新抽签。

教学提交协议以一个Tick为单位:从已提交状态Sk计算Sk+1及事件,把本Tick输入、状态版本与事件发件箱共同写入耐久事务,提交后才确认该Tick已完成并发布不可撤销结果。实时位置可以先作为带“未耐久确认”边界的预测性展示,但库存、胜负和奖励确认只来自已提交事件。写入延迟过大时停止接收超出有界队列的新操作,不能一面无限确认、一面期待事后快照补救。

后台快照先固定已提交Tick K的一份不可变状态视图:在模拟提交边界复制,或以写时复制保留该版本;随机状态、消费序号、对象状态和未交付事件均从这份视图读取。序列化期间模拟可继续推进,但不能逐对象读取正在变化的当前世界,否则文件虽标K,内容却混入K+1。

定期把已提交Tick K的完整快照写到不可变文件,核对内容哈希与可读性后,在事务中发布(K,快照引用,日志起点K+1)。日志先保留到该引用成功发布,再删除K以前已被快照覆盖的部分。若快照文件写完、引用提交前崩溃,旧引用和旧日志仍能恢复;若引用已提交,文件必须已持久可读。只写一个最新Tick数字却先删除旧日志,会失去重建状态所需的数据。

替代进程先通过条件提交取得新epoch,然后加载已发布快照,按日志顺序重放至最后耐久Tick,恢复未发送事件与输入去重结果,校验状态哈希后才开放Ready。重放期间不直接再次发奖,已提交发件箱按原事件ID交付,下游幂等。重连玩家拿到新路由、完整状态和已处理序号,再补发未确认输入;旧连接包与旧实例结果都因会话或epoch失效被拒绝。

例如快照保存到100,日志101到105已耐久提交,A在发送105确认前退出。B取得epoch8后从100重放到105,客户端重发105返回原结果,再执行106。日志106若只写入缓存而未提交,恢复停在105,106由客户端重试执行一次。状态或日志未能满足确定重放时,恢复校验会发现哈希不符,保留数据并按对局故障策略终止,不能以“恢复成功”跳过不一致状态。快照、日志与固定模拟规则共同提供恢复能力,单靠服务发现和新路由不具备这项能力。

恢复数据的退休条件

模拟日志和业务去重结果有不同消费者。快照覆盖Tick100以后,1到100的模拟输入可以从重放日志退休,但客户端可能尚未收到输入100的成功回执,奖励发件箱也可能仍在重发。快照因此保存活动输入会话的消费位置、保留窗口内的请求摘要与原结果、未交付事件及其ID。日志截断只删除已经由快照承接的数据,不能顺手清空去重表和未确认发件箱。

例如输入100扣除最后一发弹药并提交成功,快照也保存了弹药0;若恢复只保留余额与ACK,却丢失100的原结果,重试会因无弹药被拒绝,与已经发生的射击矛盾。恢复后的旧ID先返回保存结果,新ID才检查当前弹药。超出结果保留窗口时返回要求重同步或历史查询入口,同时维持旧会话关闭信息,使旧输入不可能再次变成新操作。

验证方法

服务重启、重复请求与数据库超时分别注入,检查交易幂等和实例唯一权威。监控同时记录Tick、队列和连接,健康不能只看端口。

实现与证据

会话路由为设计伪代码。本页给出epoch与结果ID的不变量,尚未部署网关、实例仲裁与持久化服务;服务故障和容量数字均待目标环境测量。

相关主题

参考资料