Skip to content

热更新体系 ​

Category ​

system / 脚本与热更子树。节点定位:热更新体系——解决什么问题、一条下载链之外的能力清单、兼容性是最危险问题、回滚与灰度优先级、与测试的关系。

Definition ​

热更新最吸引人的地方,是它看起来能把“发现问题到修复生效”的时间压得很短。对长线运营项目来说,这种能力的确非常重要,因为很多问题根本等不起完整发版和平台审核。但热更新真正危险的地方也在这里:它缩短的是修复窗口,不是测试责任,更不是设计责任。

如果没有版本兼容、签名校验、灰度发布和快速回滚能力,热更新不会让系统更安全,只会让错误更快到达更多玩家手里。

Problem ​

热更最容易被当成「下载成功就完事」,实际最危险的是兼容性:校验全绿也挡不住灰度桶崩掉。回滚能力必须优先级极高,灰度不是附加优化而是事故隔离机制——没有回滚的热更等于没有刹车。

热更新真正解决什么问题 ​

热更新最适合解决的是这些现实压力:

  • 平台发版周期长
  • 运营活动频繁切换
  • 线上问题需要快速止损
  • 某些脚本、配置、资源改动不值得整包发版

它的本质不是“让所有东西都可变”,而是给系统建立一条比整包发布更快的受控修复链路。

一套热更新体系通常不只是一条下载链 ​

成熟的热更新体系,通常至少包含下面几层能力:

  • 补丁内容组织
  • 版本与依赖关系管理
  • 下载、校验和签名验证
  • 灰度和分批放量
  • 回滚与失效策略
  • 与宿主版本的兼容判断

如果这些能力没有一起建立,所谓热更新通常只是“远程替换文件”,不是真正可运营的体系。

最危险的问题是兼容性,而不是下载失败 ​

很多团队做热更新时,最先盯的是补丁大小和下载成功率,但真正容易出大事故的往往是兼容性:

  • 新脚本依赖了旧宿主没有的接口
  • 新配置要求的字段旧代码不认识
  • 新资源引用了未下发的依赖
  • 补丁之间存在顺序依赖,却没有被正确表达

这类问题之所以危险,是因为补丁可能下载和校验都成功了,但运行时才开始崩。

回滚能力为什么必须优先级极高 ​

没有回滚的热更新,本质上是在把线上当试验场。真正可用的热更新体系,必须回答:

  • 补丁出问题时能否快速停用
  • 客户端是否能退回上一稳定版本
  • 回滚后状态和缓存如何处理
  • 服务端是否知道客户端当前补丁层级

这些问题如果没被提前设计,事故发生时团队往往只能二次发补丁去修补丁,风险会继续叠加。

灰度不是附加优化,而是事故隔离机制 ​

热更新之所以需要灰度,不是因为“这样更专业”,而是因为任何补丁都可能存在真实世界里的兼容差异。设备型号、网络环境、历史版本、缓存状态都会影响结果。灰度的价值就在于:

  • 先缩小影响面
  • 先观察错误率和兼容性
  • 先验证补丁链是否真的可恢复

没有灰度,热更新的事故半径通常会远大于整包发版。

热更新和测试是什么关系 ​

热更新能缩短修复路径,但它从来不能替代测试。更准确的说法是,热更新会让你更需要分层测试:

  • 补丁内容自身测试
  • 与当前宿主版本的兼容测试
  • 与历史补丁链的回归测试
  • 回滚路径和失效路径测试

如果团队把热更新当成“线上修就是测试”,那它最终只会变成制造更多线上状态组合的来源。

一个更稳妥的体系化做法 ​

比较稳妥的热更新体系通常会做到:

  • 补丁内容有清楚边界,知道哪些可热更,哪些必须整包
  • 补丁与宿主版本之间有明确兼容矩阵
  • 发布链路内建签名、灰度、观测和回滚
  • 服务端知道客户端所处的补丁版本和能力层级

这套体系真正重要的,不是让修改更快,而是让快速修改依然在可控范围内。

常见误区 ​

最常见的错误,是把热更新当成测试替代品,觉得有问题可以线上再修。第二类错误,是只建设下载和替换能力,不建设兼容、灰度和回滚能力。第三类错误,是热更新边界没有先定,结果脚本、配置、资源和宿主接口一起跨版本漂移,最后补丁链本身变成事故来源。

实战案例:一次「校验全绿,灰度桶崩了四倍」的热更兼容排查 ​

案例为教学示例(非摘自真实项目),数字经过取整简化。它把本页「一套热更新体系通常不只是一条下载链」「最危险的问题是兼容性,而不是下载失败」「回滚能力为什么必须优先级极高」「灰度不是附加优化,而是事故隔离机制」「常见误区」用到深水区:热更新的最后一道验收不在下载校验,而在兼容矩阵与补丁层级——这是一次兼容缺口被灰度桶拦下的完整排查。

背景与现象:一款长线运营项目,宿主大版本 2.4~2.7 并存(最老支持 2.4)。为修活动结算脚本的一个发奖错误,发布脚本热更补丁;下载、校验、签名全绿,按设备分桶灰度 5%,40 分钟后灰度桶告警:崩溃率 0.3% → 1.2%(4 倍),其余 95% 用户尚未暴露。

第一步:现象与口径——先把「崩了四倍」按宿主分桶 ​

口径统一:崩溃率取崩溃平台的会话崩溃占比口径,按宿主大版本分桶对照。补丁前后各记一笔:

  • 补丁前:全量基线 0.3%
  • 灰度桶:1.2%,增量 0.9 个百分点(4 倍)

增量不是均匀分布的:崩溃堆栈 100% 落在新脚本的调用栈上,宿主分布集中在 2.4/2.5——先分桶,再算兼容账。

第二步:分层归因——从下载校验到运行时的补丁链账本 ​

text
传输账   补丁下载成功率 99.8%、签名校验 100% 通过——传输层无责
版本账   灰度桶宿主分布:2.6+ 占 60%(崩溃率 0.2%),2.4/2.5 占 40%(崩溃率 2.7%)
合成账   60%×0.2% + 40%×2.7% = 1.20%——与桶内实测 1.2% 对上,崩溃集中在老宿主
接口账   崩溃栈顶是活动配置门面接口——它在宿主 2.6 才暴露,2.4/2.5 一调用就抛
止损账   告警到停用下发通道 22 分钟;原计划 T+2 小时全量,灰度桶拦下 95% 暴露面

账本收束:传输链路全绿(下载 99.8%、签名 100%),崩溃全部来自运行时兼容缺口——新脚本调用的门面接口只在宿主 2.6+ 存在,占桶 40% 的 2.4/2.5 一进活动就抛。本页「补丁可能下载和校验都成功了,但运行时才开始崩」逐字应验。

第三步:根因——兼容矩阵缺了「脚本 API 最低宿主版本」这一行 ​

对照本页「一套热更新体系通常不只是一条下载链」的六层能力:内容组织、下载校验签名、灰度放量都在,缺的是「与宿主版本的兼容判断」没管到接口粒度——兼容矩阵只登记「补丁 ↔ 宿主大版本可否安装」,没登记「脚本调用的每个门面接口最低宿主版本」,2.4 这样的老宿主就漏进了灰度桶。回滚四问对照更难堪:「服务端是否知道客户端当前补丁层级」当时答不出——服务端只知道「装没装」,不知道「装到哪一层」,停用通道只能阻断新加载,已加载端要靠重启走本地兜底回落。本页「没有灰度,热更新的事故半径通常会远大于整包发版」反向成立:拦住这次事故的正是 5% 的桶;而常见误区第三类「脚本、配置、资源和宿主接口一起跨版本漂移,最后补丁链本身变成事故来源」就是这次差点应验的形态。

第四步:处置与回填——停通道是止血,回补丁层才是恢复 ​

修法分两段。应急三步:停用补丁下发通道(告警后 22 分钟);已加载端引导重启、回落到上一稳定补丁层——活动缓存按补丁层版本化,回落无脏状态;为 2.4/2.5 单出兼容版补丁(接口存在性守卫 + 降级路径),按同分桶灰度重放。复核:停用后 30 分钟崩溃率回落至 0.3% 基线;兼容版重放同分桶持平后放量。制度三步:脚本 API 最低宿主版本写入兼容矩阵,发布流水线按矩阵拦截(缺登记不发);回滚演练细到补丁层粒度,服务端登记每端所处层级;分层测试清单补上「与当前宿主版本的兼容测试」,矩阵内最老两个大版本必跑冒烟。

回填清单:

落点动作回填处
兼容门禁脚本 API 最低宿主版本入兼容矩阵,流水线按矩阵拦截发布最危险的问题是兼容性,而不是下载失败
层级回滚回滚演练细到补丁层,服务端登记每端所处补丁层级回滚能力为什么必须优先级极高
灰度隔离兼容类补丁灰度桶必含最老支持宿主,观察期按宿主分桶看崩溃灰度不是附加优化,而是事故隔离机制
兼容测试「与历史宿主版本兼容」进分层测试清单,最老两个大版本必跑冒烟热更新和测试是什么关系

案例的三个教训 ​

  1. 校验全绿只证明补丁完整送达,不证明它能在每个宿主上跑——兼容矩阵要细到接口粒度,它是热更的最后一道验收。
  2. 灰度是事故隔离机制,不是流程仪式——5% 的桶把一次接口缺口摁在了 95% 用户之外。
  3. 停用通道只是止血,回滚到补丁层才是恢复——服务端答不出「每端装到哪一层」,止损就永远缺一半。

Used By ​

热更体系设计;灰度与回滚机制评审;热更兼容回归流程。

  • KBEngine(commit 0bc93d5):脚本级热重载的实现样例——baseapp/cellapp 注册 reloadScript(kbe/src/server/baseapp/baseapp.cpp:445、kbe/src/server/cellapp/cellapp.cpp:175),核心虚函数逐实体重载(kbe/src/lib/server/entity_app.h:1452,实现 :1441-1447(同文件))——重载的是脚本而非 C++
  • Nakama(commit e920249):对照的「重启式」边界——三语言运行时 Lua/JS/Go(内置 gopher-lua fork server/runtime_lua.go:37、goja go.mod:12),但 Reload/hotswap 在 server/ 全量 grep 无命中,脚本更新走进程重启

关系链:解决什么 → 能力清单 → 兼容性 → 回滚 → 灰度 → 测试关系 → 体系化做法 → 常见误区 → 实战案例。上游:scripting/03(热更的对象)。下游:production/03(灰度回滚与兼容窗口的发布面)。

Reference ​

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

游戏知识体系