这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
《MMO 服务器开发》8.1 Redis 使用与失效策略
缓存与数据库不一致是游戏服务端最常见的事故源。本节用真跑的 Redis 讲清三件事:缓存键怎么设计成命名空间加版本号、写库和删缓存到底谁先谁后、延迟双删怎么覆盖并发窗口;并给出并发读写下缓存与源最终一致的可运行断言、本机实测数字,以及失效风暴的三种摊平手段。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
缓存与数据库不一致是游戏服务端最常见的事故源。本节用真跑的 Redis 讲清三件事:缓存键怎么设计成命名空间加版本号、写库和删缓存到底谁先谁后、延迟双删怎么覆盖并发窗口;并给出并发读写下缓存与源最终一致的可运行断言、本机实测数字,以及失效风暴的三种摊平手段。
时钟回拨是雪花算法唯一的软肋。本节讲清它的三种成因(NTP 步进校时、虚拟机迁移、闰秒回拨)与三种应对(等待追平、换 worker id、抛错拒绝)各自的代价,并给出一个完整可跑、可注入假时钟的 L1 容错生成器与五条测试用例,让回拨从线上偶发变成可参数化的测试场景。
号段模式:用 Redis INCRBY 一次批发一批 ID,在内存里用双 buffer 预取慢慢发,把发号的远程调用摊薄到步长分之一。讲清主段与备用段切换的临界区、并发安全与跳号取舍,并在本机真连 Redis 跑出两个实例共用同一号段的互斥断言。
从零实现雪花算法:把 64 位切成符号位、41 位时间戳、10 位 worker id 与 12 位序列,讲清 worker id 如何由区服号合成、同毫秒序列耗尽怎么处理、为什么吞吐天花板由位布局而非核数决定,并给出本机实跑的并发吞吐与重复率 0 断言。
战场奖励到底算在哪个服?本节给出跨服结算的归属规则:战场只产出结算单,母服才是账本。用幂等键保证同一张单子重传多少次都只发一次奖,回传失败自动重试,最后用「战场产出总量等于各母服入账总量之和」的对账兜底,把丢单和重复入账都当场抓出来,并给出可直接运行的幂等与守恒断言。
跨服消息怎么找到人?频道广播靠「谁订阅了哪个频道」扇出,定向私聊则要先把玩家路由到它当前所在的服。本节用 NATS 真跑三个服:世界频道只到订阅它的服,定向消息精确投进目标服的收件箱,掉线期间的消息先暂存、换服上线后一次性补投,全程用独立 subject 前缀隔离。
玩家在 A 服报名,却要和 B、C 服的对手同场。本节从零写一个跨服中心:报名带服号与快照落到跨服队列,按战力分桶、蛇形分队完成撮合,结果再沿报名时那条长连接精确推回各自的母服。最后真起三个模拟服进程,断言通知回对了服,并给出并发写同一连接导致帧交错的真实踩坑与修法。
实例进程会崩,内存里的场景状态会随之消失。本节用快照 + 事件重放把状态重建出来:事件覆盖所有状态变更、快照带校验和、恢复时选最新可用快照再补放事件;并用 aegis 真跑「杀掉进程 → 重建 → 断言指纹一致」,最后讨论重建期间玩家怎么办。
上一节的调度器能起实例,但没有回答「起在哪台机器」。本节做分配 Broker:用 CPU、内存、连接数、实例槽位四个维度评估容量,选出余量最大的节点并立即预留,开不出来时进队列等待、超时返回明确错误;并给出 aegis 在本机的容量评估基准数字。
场景服、战斗服、世界服三类实例由同一个调度器管理:分配 ID、起进程、等到就绪、推进生命周期。本节把实例状态机(创建→就绪→分配→使用→回收)写成一张只有合法迁移的表,并用 aegis 真起一个实例进程、读到 READY 后再断言它确实在监听。
网关重启不该把在线玩家直接踢下线。本节实现连接排空:停止接受新连接、广播 CmdDrainNotice 0x1001、下发 CmdReconnectTicket 0x1002、等旧连接自然结束、超时兜底,并真起进程 + 客户端跑完整排空流程,含千连接错峰压测。
新连接该被分到哪个网关?会话粘性有三种实现:一致性哈希、目录查表、客户端重连票。本节把前两种从零实现,量出「摘掉一个节点时迁移多少人」并给出可判定的取舍结论,再讲清网关滚动发布期间在线玩家到底会看到什么、哪种组合才能做到发布无感。全部代码在本机真跑。
网关集群里,玩家的长连接只挂在某一个网关上,路由目录记录「谁在哪」。本节从零实现带代次的 RouteDirectory,把「玩家换服时旧连接迟到断开」这个经典竞态钉死,并真起多个网关进程与客户端,断言目录指向正确节点、旧连接不会误删新绑定、换服后旧代次解绑被拒。
3.1 与 3.2 的实现都在 Go 上,真实的 MMO 项目却常常是双栈:新模块用 Go,历史模块是 Java。本节用同一份 aegis 帧,在 Go 与 Java 21 上各手写一次 gRPC 服务定义(不装 protoc),真跑往返并对照两种栈在本机的量级,并记下 Java 侧容易踩的一个坑。
超时是未知,那么重试就必须先有幂等前提;下游持续失败时靠熔断止损,过载时靠背压把请求挡在门外而不是排队等雪崩。本节用 Go 真跑重试策略、三态熔断与令牌桶,并给出「熔断打开前后」上游与下游的实测对照,说明为什么这三件事必须成套使用,全部在本机实跑验证。
跨进程调用的第一个问题不是选框架,而是一次调用最多能花多久。本节复用卷一的 aegis 帧做跨进程载荷,把客户端 deadline 拆成每一跳的超时预算,再用真跑的 gRPC 往返说清「超时了业务算成功还是失败」,以及为什么必须有幂等键,全部在本机真跑验证。
同一套注册-发现-健康检查,在 etcd、NATS、Consul 上各实现一遍并真连本机中间件跑出数字:注册延迟、发现延迟、心跳中断后的下线收敛时间。三种模型(租约、心跳陈旧判定、原生 TTL 检查)的差异不在毫秒,而在谁负责判定存活这件事上,据此给出可判定的选型结论。
单写者是集群里最难保证也最值钱的性质。本节用 etcd 的 TTL 租约加一次事务 CAS 从零实现 Leader 选举,讲清为什么「键不存在才写」就等价于互斥,再用单调递增的 fencing token 挡住醒过来的旧 leader,真连本机 etcd 断言同一时刻只有一个 leader。
服务发现的核心不是选哪个中间件,而是把「存活」讲清楚:节点用 TTL 租约注册、靠心跳续约维持,etcd 在租约过期时自动删键。本节从零实现注册、续约与健康三态(健康 / 降级 / 下线),并用 Watch 驱动的监视器真连本机 etcd 跑通全部断言。
把整簇跑起来之后,它到底能扛多少?本节用 B 层基准本机实跑:四个节点、单网关千连接、跨服匹配吞吐、跨服消息延迟 P50/P90/P99,落 JSON 并逐条与正文数字对应。沿用基准三条纪律,报同机相对量级,禁用「快 N 倍」式绝对话,最后合成一个集群总承载。