Skip to content

扩容、缩容与动态加服 ​

Category ​

server / capacity 子树。节点定位:扩缩容的难点在状态层不在资源层——会话在内存、进度在进程、分区在数据;动态加服相对安全、缩容比扩容难、全局组件要联动。

Definition ​

扩缩容听起来像资源操作:加机器、减机器、调配额。但对游戏服务来说,真正的难点从来不在资源层,而在状态层——玩家会话在内存里,进度在进程里,分区边界画在数据里。无状态服务的扩缩容可以近乎随心,有状态服务的每一次扩缩都要回答同一个问题:状态怎么办。回答不了这个问题的弹性方案,上线第一天就会用掉线来纠正认识。

动态加服是游戏行业里最常用也最安全的扩容形态,因为新服不碰旧数据。理解了加服为什么容易,就能反过来理解缩容、合并为什么难:前者只增加不改动,后者要触碰既有状态的归属和边界。

Problem ​

无状态服务的扩缩容可以近乎随心,有状态服务的每一次扩缩都要回答同一个问题:状态怎么办。回答不了这个问题的弹性方案,上线第一天就会用掉线来纠正认识——缩容比扩容难,正是因为它要搬状态。

先分清哪些组件真正能弹性 ​

一套游戏后端里,组件的弹性空间差异极大,笼统谈“自动扩缩容”没有意义:

  • 接入与代理层:无状态或会话可转移,通常可以随时横向伸缩。
  • 匹配、聊天、公告类公共服务:请求即处理,处理完即释放,弹性收益最大。
  • 房间服:状态随对局生灭,对局结束后进程可回收,属于半弹性。
  • 主城与角色逻辑服:玩家会话和数据绑定在进程上,运行中不能随意增减。
  • 存储层:扩容涉及数据分布,通常要提前规划,临时扩不动。

弹性方案应该按组件逐个设计,而不是给整个后端套一个统一策略。实践中常见的分层做法是:无状态层交给通用弹性设施,房间层用对局生命周期驱动调度,角色逻辑层靠开服和迁移这类专门流程处理。

水平扩容的前提条件 ​

水平扩容的前提不是有机器,而是系统本身具备被扩容的结构条件:

  • 可分片:负载能被切成互不干扰的单元,比如按区服、按房间、按场景。
  • 可路由:请求能被正确地送到对应分片,路由信息要能动态更新。
  • 单写者边界清晰:同一份数据同一时刻只归一个实例管,扩容不会产生双写。
  • 新实例可就绪:新实例启动时能拿到需要的配置、数据和注册,不需要人工介入。
  • 扩容可回退:扩容操作本身要能撤销,发现扩错方向时能退回原状而不是将错就错。

这四个条件里缺任何一个,扩容都会卡在对应的位置:缺分片设计,加机器只是加了个空壳;缺路由更新,玩家还是连到旧实例;缺单写者约束,扩容瞬间开始出现数据竞争。这些结构条件平时就要备好,扩容事件发生时来不及补。

动态加服为什么相对安全 ​

开新服是游戏扩容的主力手段,它的安全性来自一个简单的事实:新服是纯新增,不改任何既有数据。即使如此,加服仍然有一套值得遵循的工程要求:

  • 开服流程自动化:建库、灌基础数据、配置初始化、注册进列表服和网关路由,整个流程脚本化,几分钟内完成。
  • 预建资源池:镜像、模板、数据库账号提前备好,高峰期临时建资源是开服延误的常见原因。
  • 开服洪峰纳入容量:新服开放瞬间的涌入往往数倍于平时,容量按洪峰而不是均值准备。
  • 全局组件联动:每开一个服,匹配、跨服、聊天这些全局服务的容量假设都在变,需要联动校验。

加服真正的风险不在新服本身,而在它对全局组件的累积压力。单个服无足轻重,连开二十个服之后,跨服玩法的负载假设可能已经被悄悄打破。

缩容为什么比扩容难 ​

扩容是加东西,缩容是拿走正在被使用的东西,方向相反,难度不对称:

  • 玩家在线时不能缩:会话在内存里,强行回收就是掉线和状态丢失。
  • 空服判定要防抖动:短暂在线低谷不等于真没人,缩错要付出召回成本。
  • 数据不能随手删:低活跃服的数据是玩家资产,缩容的只是计算层,存储通常原地保留。

常见做法是把缩容变成有明确门槛的运营决策,而不是自动动作:持续观察一个服的活跃水位,低于阈值后进入观察名单,确认趋势后再执行合并或下线这类专门流程。自动缩容留给真正无状态、无资产的那一层。

弹性伸缩的适用边界 ​

弹性伸缩在游戏后端的名声两极,原因通常是适用边界没划清。收益明显的场景往往有这些特征:

  • 负载单元小而多:房间、对局、战斗实例,单 unit 便宜,可以高频创建销毁。
  • 生命周期由业务事件驱动:对局开始创建、结束销毁,不需要人工判断时机。
  • 状态可快照或可丢弃:中断的代价可控,或者根本不存在长命状态。

反过来,负载单元大、状态厚、生命周期长的组件(典型的如角色逻辑服),套用同一套弹性模型,得到的多半是事故而不是收益。判断一个组件适不适合弹性,先看它的状态能不能在几秒内被安全转移或重建,答案是否定的,就不要勉强。

全局组件的容量联动 ​

区服数量是很多全局组件容量的隐变量,加服决策却常常只算新服本身的账。联动校验至少要覆盖:

  • 跨服玩法:参服越多,战场汇聚和匹配池越大,按全区在线校验而不是按单服。
  • 全局聊天与社交:广播规模随总在线增长,和单服在线无关。
  • 匹配与排行服务:数据量和更新频率随区服数线性上升。
  • 公共存储:账号、列表、订单这类全区共享数据的压力同步上涨。

一个可操作的纪律是:每次加服批量超过一定规模,就把全局组件的容量假设重算一遍。全局组件平时默默无闻,出事时的爆炸半径却是全区的。

加服节奏与运营日历 ​

加服不只是技术动作,它和运营节奏咬合在一起,开服时机本身就是运营策略的一部分:

  • 集中开服日(周五晚间、节日前)意味着洪峰叠加,容量要按最坏叠加情况准备。
  • 开服间隔过密会稀释单个新服的人数,同时抬高全局组件的常态化压力。
  • 版本大更新前通常不宜开服,新服玩家第一时间遇到的是尚未稳定的版本。
  • 开服时刻点也有讲究:放在玩家空闲充足的时段,比放在流量高峰中段更能平滑涌入。

技术侧与运营侧共享一份开服日历,是低成本高回报的协作方式。容量预案按日历提前滚动准备,比事件驱动的临时准备从容得多,也给演练和资源预留留出时间。

扩容预案与演练 ​

扩容预案的价值,取决于它是否被当成活文档维护。一份只在事故复盘时才被翻出来的预案,通常已经和现状脱节。值得保持的习惯包括:

  • 预案写明触发条件、执行步骤、责任人和验证方法,而不是一句“扩容即可”。
  • 定期演练:按预案真实扩一轮,验证流程耗时和每个环节的可用性。
  • 资源预留动态化:预留多少机器、留在哪里,跟着运营日历调整,大版本和节假日之前提前备量。
  • 扩容后的观察项预定义:哪些指标说明扩容生效,哪些指标说明瓶颈在别处。

演练中发现的流程断点——权限没开、镜像过期、模板字段没人维护——平时修是几分钟的事,事故现场修就是半小时起步。预案不演练,等于只有纸面容量。

常见误区 ​

  • 把扩容理解成加机器,忽略了路由、分片和单写者这些结构前提。
  • 对有状态组件套用无状态的自动伸缩策略,用掉线换来教训。
  • 加服只算新服的账,全局组件的容量假设被连开二十个服悄悄打破。
  • 缩容交给自动策略,在低谷期回收了正在回暖的服务器。
  • 预案写成文档,从未演练,出事时每个环节都在现场第一次跑。

扩缩容能力的成熟度,最终体现在响应速度和安全性能否同时成立:扩得快而且不出事,靠的不是现场发挥,而是结构条件早就备好、预案早就跑通。容量工作的大部分价值,都是在没有容量事件的时候完成的。

实战案例:一次「晚高峰一到,三个服同时掉线」的弹性边界排查 ​

案例为教学示例(非摘自真实项目),数字经过取整简化。它把本页「先分清哪些组件真正能弹性」「缩容为什么比扩容难」「全局组件的容量联动」用到深水区:弹性策略照抄了通用做法,掉线来纠正认识——这是对有状态组件套错弹性的排查,也是「缩容门槛与全局联动」的完整展开。

背景与现象:某页游接入自动伸缩平台,按「CPU 低于 30% 持续 10 分钟即回收」的策略统一套到全部服务。周日 19 点晚高峰刚起,三个角色逻辑服被判定闲置回收,6000 多名在线玩家同时掉线,会话在内存里无处恢复,回档 15 分钟。

第一步:现象与口径——先分清缩的是哪一层 ​

缩容口径:判定取的是 CPU 而不是在线人数——午后低谷触发的缩容排队到晚高峰才执行;回收的三个服当晚在线率正在爬升,属于「短暂低谷不等于真没人」的典型。

状态口径:玩家在线时不能缩:会话在内存里,强行回收就是掉线和状态丢失——角色逻辑服的会话和进度全在进程上。

全局口径:当月连开 20 个服,匹配池和全局聊天规模翻倍,但加服决策只算了新服自己的账,全局组件容量假设没人重算。

第二步:分层归因——按本页弹性边界比对 ​

text
掉线事故归因  占比
  有状态组件套用无状态自动伸缩       52%
  缩容无防抖门槛,低谷判定晚到执行    29%
  加服只算新服账,全局组件未联动      19%

逐项比对:角色逻辑服属于负载单元大、状态厚、生命周期长的组件——命中「套用同一套弹性模型,得到的多半是事故而不是收益」;缩容没有观察名单和防抖——命中「缩错要付出召回成本」;跨服匹配延迟随区服数上涨,「加服只算新服的账,全局组件的容量假设被连开二十个服悄悄打破」正中这条。

第三步:根因——给整套后端套了一个统一弹性策略 ​

弹性方案没有按组件逐个设计:接入层和房间层能弹,不等于角色逻辑服能弹。判断一个组件适不适合弹性,先看它的状态能不能在几秒内被安全转移或重建——角色逻辑服的答案是「不能」,却挂在了同一个自动策略下。

第四步:处置与回填——分层定弹性,缩容改决策 ​

  1. 角色逻辑服退出自动伸缩:缩容改为观察名单加人工合并流程,活跃水位连续两周低于阈值才进名单。
  2. 无状态层保留弹性:接入代理、匹配、公告继续自动伸缩,自动缩容只留给真正无状态无资产的那一层。
  3. 加服触发全局重算:每次加服批量超过 5 个,重算匹配、聊天、公共存储的容量假设。
  4. 扩容预案演练:按预案真实扩一轮,验证镜像、权限、模板每个环节的可用性。

复核账:掉线事故 1 起 → 0 起;全局匹配池按全区在线校验后扩容 0 次过载;演练提前发现镜像过期与权限缺失两个断点,事故现场修复成本归零。

回填清单:

回填项动作回填处
组件弹性分级接入/房间半弹性自动化,角色逻辑服走专门流程先分清哪些组件真正能弹性
缩容防抖观察名单、两周趋势确认、人工合并,不设自动缩容缩容为什么比扩容难
状态转移判据状态能否几秒内安全转移,答案是否就不勉强弹性弹性伸缩的适用边界
全局联动加服批量超 5 个即重算全局组件容量假设全局组件的容量联动

案例的三个教训 ​

  1. 弹性是逐组件的属性,不是平台开关。状态厚的组件套自动伸缩,用掉线换来教训。
  2. 缩容比扩容难,因为它拿走正在被使用的东西。低谷判定要防抖,缩错要付召回成本。
  3. 加服的账要算到全局。单个服无足轻重,连开二十个服,跨服组件的负载假设可能已被悄悄打破。

Used By ​

扩缩容方案评审;动态加服设计;缩容与状态迁移规划。

  • Agones(commit 5366fb7):容器编排路线的对照样本——K8s 上托管与伸缩专用游戏服(README 定位「hosting, running and scaling dedicated game servers on Kubernetes」,README.md:9),Fleet 滚动更新 rollingUpdateDeployment(pkg/fleets/controller.go:441),分配服务 Allocate(pkg/gameserverallocations/allocator.go:202),生命周期状态 Ready/Reserved(pkg/apis/agones/v1/gameserver.go:54-66),游戏进程经旁路 sidecar SDK 上报(cmd/sdk-server/main.go:15)

关系链:状态难点 → 弹性前提 → 动态加服 → 缩容 → 适用边界 → 全局联动 → 加服节奏。上游:capacity/01(容量模型)。下游:server/capacity/03(迁移合服)。

Reference ​

本页为工程经验归纳;页内实战案例为教学示例(数字取整简化)。

游戏知识体系