服务端代码热更新调研与设计
状态
Proposed(调研完成,设计定稿,分阶段实施)。P0 通道已落地(热更包托管 + agent 下发确认),框架适配器 P1 起逐个接入。
1. 问题与边界
游戏服在维护窗口外修 bug/改数值/调逻辑,需要不重启进程把新代码换进去。这与两个既有系统相关但不同:
- 版本发布系统(release-management-design.md):面向客户端资源包(玩家设备下载);
- 本文热更新:面向游戏服务器进程内部代码(skynet 的 lua、KBE 的 python、JVM 的 class、Node 的 JS)。
平台层不碰框架内部魔法(那需要每框架深度适配),而是提供统一的热更通道:包托管、下发、确认、回滚、审计——框架各自的 reload 机制作为可插拔适配器。
2. 各框架热更新机制全面对比
2.1 Lua 系
| 方案 | 机制 | 粒度 | 热更限制 | 评价 |
|---|---|---|---|---|
| skynet | 官方无内置 hotfix;社区惯例 require 新模块文件 + clearcache,或云风 inject(inject.lua 遍历替换函数 upvalue) | 函数级 | 闭包 upvalue/协程栈内旧引用替换复杂;C 模块不可热更 | 手游服务端事实标准;inject 方案成熟但约定大于框架 |
| xLua(Unity 客户端) | lua_hotfix 替换函数 | 函数级 | 仅客户端;服务端无关 | 列出供对照 |
| OpenResty/lua-nginx | content_by_lua + reload worker | 文件级 | 平滑重启 worker,非进程内替换 | 客户端接入层场景 |
| tlua/srlua 变体 | 自行开发沙箱替换 | - | - | 少数自行开发引擎 |
2.2 Python 系
| 方案 | 机制 | 粒度 | 热更限制 | 评价 |
|---|---|---|---|---|
| kbengine | 无官方热更;社区做法 reload(module) + CellApp/BaseApp 手动重绑;KBE 的 Entity 脚本层理论上可 importlib.reload | 模块级 | 已实例化 Entity 持旧类引用,需遍历 __class__ 重指;C 扩展不可 | 国内 MMO 常用;操作危险系数高,通常只热数值 |
| BigWorld(KBE 前身) | 官方支持 Entity 脚本重载(bigworld.reloadScript),引擎级重绑 __class__ | 类级 | 商用引擎已停更;机制最完善的历史方案 | 设计参考价值高 |
| PurePython(gunicorn --reload / watchdog + importlib) | 进程外重启或模块 reload | 进程/模块 | 数据序列化桥接 | 小型服常用"软重启"替代热更 |
2.3 Java 系
| 方案 | 机制 | 粒度 | 限制 | 评价 |
|---|---|---|---|---|
| Arthas redefine/retransform | Instrumentation API 字节码替换 | 类级 | 不能改字段签名;新类需 retransformClasses;调试向 | 阿里系,事实标准;但定位线上诊断而非常态热更 |
| Spring Boot DevTools | classloader 重启 | 进程级(快重启) | 丢失堆内状态 | 开发期工具 |
| OSGi/SoFAArk | 模块 classloader 隔离+替换 | bundle 级 | 架构侵入大 | 重架构,新项目少用 |
| 热部署 agent(自行开发 Instrumentation premain) | attach 替换 jar | 类级 | 同 Arthas 限制 | 平台需对接的就是这类 agent |
2.4 JavaScript/TypeScript 系
| 方案 | 机制 | 粒度 | 限制 | 评价 |
|---|---|---|---|---|
| Node.js | 无内置;require.cache 删除重载(CommonJS)或 --experimental-vm-modules 隔离 context | 模块级 | ESM 缓存不可删;闭包引用同 Lua 问题 | H5/小游戏服常见;Pomelo/裸 Node 均此路 |
| jsb/WebSocket 引擎(Cocos Creator 服务端) | 自行开发模块管理器替换 | 模块级 | 少量自行开发 | - |
| QuickJS 嵌入式(skynet-js 等) | 重建 JS context | context 级 | 状态迁移需显式 | 新兴,量少 |
2.5 C++/其他
| 方案 | 机制 | 评价 |
|---|---|---|
| 动态库 dlopen/dlsym 替换 | .so 重载 | 危险,极少生产使用;Plywood/自行开发引擎 |
| etcd/配置驱动"热逻辑" | 逻辑外置为数据(表达式/规则/DSL) | 不是代码热更但达成同一目的,见配置热更新设计文档 |
| Go 游戏服 | 不可热更(静态编译);社区方案是 so 插件/热重启(graceful + fd 传递) | croupier 自身即 Go,运维面用滚动重启 |
2.6 横向总结
| 维度 | Lua(skynet) | Python(KBE) | Java | JS(Node) |
|---|---|---|---|---|
| 成熟热更生态 | ★★★★(inject 惯例) | ★★(BigWorld 血统有但社区弱) | ★★★★(Arthas) | ★★ |
| 推荐粒度 | 函数/模块 | 模块(仅数值类安全) | 类(不改签名) | 模块(CJS) |
| 状态保持难点 | upvalue/协程 | __class__ 重指 | 字段签名 | require.cache/闭包 |
| 平台对接面 | agent 上放包+触发 reload RPC | 同左 | attach agent jar | 同 Lua |
| 安全热更范围 | 逻辑函数;数值必安全 | 数值/纯函数 | 方法体 | 纯函数模块 |
3. 设计
3.1 总体:通道平台化,机制适配器化
管理台(croupier) 游戏服宿主
┌─────────────────┐ ①上传热更包 ┌──────────────────────┐
│ Hotpatch 管理 │ ─────────────▶ │ croupier agent │
│ (对象存储+版本+ │ ②按区服灰度 │ └ hotpatch runner │
│ 灰度+审批+回滚) │ ◀───────────── │ └ adapter(按框架) │
└─────────────────┘ ③执行结果/心跳 │ skynet|kbe|jvm|js│
└──────────────────────┘平台职责(框架无关):包托管(复用 release 的 objstore 通道)、目标选择(区服/节点灰度)、两双人审批(复用 approvals)、执行编排(复用 task/jobs)、结果确认与自动回滚、全程审计。
适配器职责(框架相关,agent 内实现):校验包结构 → 备份当前版本 → 调用框架 reload(inject/reload/attach/require)→ 自检(ping/关键接口探活)→ 失败自动还原备份。
3.2 数据模型(平台侧)
Hotpatch
├── GameID/Env + TargetSelector(区服/节点标签,复用 assignment 选择器语义)
├── Framework skynet | kbengine | jvm | nodejs | custom
├── PackageKey objstore 键 + Checksum(强制 sha256,防中间篡改)
├── EntrySpec 框架适配器参数(如 skynet: {entry: "inject.lua"}; jvm: {classFilter: "com.acme.**"})
├── Status draft → approved(双人) → rolling(灰度执行) → applied → rolled_back / failed
├── Rollout 复用 release 的分桶思路按「节点」灰度(非设备)
├── 产物 每 agent 执行结果(成功/失败/回滚原因/日志)
└── 关联 BugID(对应缺陷追踪,修复上线闭环)3.3 适配器契约(agent 内接口)
go
type HotpatchAdapter interface {
// Validate 检查包结构是否符合框架惯例(lua 文件树/jar/zip 模块集)
Validate(pkg Package) error
// Apply 应用并返回自检句柄;必须先 Backup 再 Apply
Apply(ctx, pkg) (Verify func(ctx) error, err error)
// Rollback 还原 Apply 前快照
Rollback(ctx) error
}P1 实现顺序:skynet(lua inject)→ nodejs(require cache)→ jvm(attach)→ kbengine——按使用频率与风险排序。
3.4 安全红线
- 热更包内容平台不解析不审计语义(无法判断 lua 代码意图),所以:双人审批强制、仅限定目标集合、checksum 端到端校验、执行结果 5 分钟内自检失败自动回滚;
- 禁止热更框架核心模块(adapter 的 EntrySpec 白名单约束,如 skynet 禁止触碰
skynet.*内建); - 每次热更强制关联缺陷或工单编号(来源可溯),审计链记录操作者/包哈希/目标/结果。
4. 阶段
| 阶段 | 内容 |
|---|---|
| P0(已落地) | 平台热更通道:Hotpatch 模型 + 包托管 + agent 下发确认协议(无框架 reload,先打通管理面) |
| P1 | skynet 与 nodejs 适配器(社区惯例最成熟、风险最低) |
| P2 | jvm attach 适配器;按节点分桶灰度;自检失败自动回滚 |
| P3 | kbengine 类重指适配器;热更效果验证 DSL(应用后自动调用指定函数比对返回) |
5. Review Checklist
- 适配器必须先备份后应用,Rollback 必须可重入;
- EntrySpec 白名单在平台与适配器双侧校验;
- 热更包执行前 agent 校验 checksum 与平台记录一致;
- 未经双人审批的 Hotpatch 不可进入 rolling(复用 approvals 的强制位);
- 新增 Framework 枚举需同步适配器注册表与文档 §2 对比表。
