设计一个基于CDN回源的Addressable更新策略
解读
国内手游/小游戏上线后,包体不能随意整包提审,因此资源必须走可热更路径。Addressable 是 Unity 官方主推的“可寻址 + 热更”方案,但默认只提供本地 Build & Load 与 Remote Load 两种模式,没有内建 CDN 回源、缓存、失败重试、增量差异、灰度、回滚等商业级能力。面试官想考察的是:
- 你是否理解 CDN 回源(Origin-Pull) 在国内云厂商(阿里云 OSS、腾讯云 COS、华为云 OBS、七牛、又拍)中的真实含义——即“边缘节点未命中时,回源站拉取并缓存”。
- 能否把 Addressable 的 Catalog + AssetBundle 部署流程,与 CDN 的“首次回源 → 边缘缓存 → 304 校验 → 主动刷新”机制无缝衔接。
- 是否能在弱网、断点续传、域名劫持、跨省调度等中国特色环境下,给出可落地的异常处理与性能策略。
- 是否兼顾版本灰度、资源回滚、渠道包差异化、iOS 审核期开关等运营需求。
一句话:不是“能用 Addressable 热更”,而是“让 Addressable 在国产 CDN 上像微信小游戏 CDN 那样稳定、可灰度、可回滚”。
知识点
- Addressable 三大核心文件:
- settings.json(Editor 端配置)
- catalog.json(运行时索引,含 hash、依赖、url)
- *.bundle(AssetBundle,真实资源)
- BuildScript Packed Mode 的 Update a Previous Build 机制:增量打包仅改动差异 bundle,catalog 重新生成但可对比 hash。
- CDN 回源 301/302 重定向链:国内云厂商支持“私有 bucket + 回源站”模式,源站可以是自建 Nginx 或 GitLab Pages,边缘节点首次 404 会触发回源并缓存。
- Cache-Control: max-age=31536000, immutable 与 ETag / Last-Modified 的配合:保证 bundle 文件永不回源,catalog 文件走 max-age=60 方便灰度。
- UnityWebRequest 的 DownloadHandlerAssetBundle 与 DownloadHandlerFile 差异:前者直接解压到内存,后者可落盘做 断点续传(Range: bytes=x-y)。
- 国产 CDN 刷新接口:阿里云
RefreshObjectCaches、腾讯云FlushCdnUrl,每日免费额度 1000 条,需签名;脚本化调用要在 Jenkins + 企业微信机器人 里闭环。 - iOS 审核期开关:在 RemoteCatalogLoadTimeout 里做双通道——审核期走 本地兜底 catalog(空更新),过审后远程下发 real_catalog。
- 资源加密与签名:Addressable 支持 AssetBundle Provider 重写,可在 LoadBundle 前做 AES-CTR + HMAC-SHA256 校验,防止 CDN 被恶意上传。
答案
整体策略分 六层:版本规划、打包、上传、回源、运行、灰度/回滚。
-
版本规划
- 采用 <主版本>.<资源版本>.<patch> 三段式,如 1.7.18。
- 所有 bundle 文件名带 hash(xxHash64),例如
ui_icon_7d34a1ab.bundle,确保 同名不同内容 时 CDN 视为新文件。 - catalog 文件名固定为
catalog_1.7.18.json,不带 hash,方便 CDN 刷新。
-
增量打包
- Jenkins 定时触发
AddressableAssetSettings.BuildPlayerContent()使用 BuildScriptPackedMode 的 Update a Previous Build 路径,对比上一版 catalog。 - 仅改动 bundle 会被重新打包,未改动 bundle 直接复用,保证差异最小。
- Jenkins 定时触发
-
上传与刷新
- 产物目录:
remote_assets/1.7.18/下放置 catalog + 新增/改动 bundle。 - 使用 阿里云 OSS SDK 上传,设置 ObjectMeta:
- bundle:
CacheControl=31536000,immutable - catalog:
CacheControl=60
- bundle:
- 调用 RefreshObjectCaches 接口,仅刷新
catalog_1.7.18.json的 URL,bundle 文件永不刷新,避免边缘节点误淘汰。
- 产物目录:
-
CDN 回源配置
- 源站设为 OSS 私有 bucket,回源 Host 填 bucket.oss-cn-shanghai.aliyuncs.com,回源协议 HTTPS。
- 边缘节点首次收到
catalog_1.7.18.json请求,若未命中,302 回源拉取并缓存 60 s;bundle 文件同理,但缓存 1 年。 - 开启 SNI 回源 与 TLS1.3,解决部分国产安卓机 TLS1.2 握手失败 问题。
-
运行时加载
- 初始化 Addressable 时,把 CatalogLocationURL 指向
https://cdn.example.com/remote_assets/{version}/catalog_{version}.json,version 由 服务器动态下发,可做 灰度白名单。 - 使用 UnityWebRequest 的 DownloadHandlerFile 将 bundle 落盘到 Application.persistentDataPath/aa_cache/,文件名保持 hash,支持断点续传:
- 先 Head 请求拿 Content-Length 与本地 tmp 文件大小比对,发送
Range头。 - 下载完成做 xxHash64 校验,与 catalog 中的 hash 比对,失败则重试 2 次,仍失败标记为 “坏包” 并上报服务器。
- 先 Head 请求拿 Content-Length 与本地 tmp 文件大小比对,发送
- 加载完成后,把 catalog 版本与本地缓存版本写入 PlayerPrefs,下次启动优先用本地缓存,仅当 catalog 有更新才走网络。
- 初始化 Addressable 时,把 CatalogLocationURL 指向
-
灰度与回滚
- 在 后台运营系统 配置 白名单账号 + 渠道包标签,下发 version 字段。
- 若线上出现 严重资源 BUG,立即在 OSS 侧 拷贝上一版 catalog 文件 覆盖当前版本(同名覆盖),并调用 CDN 刷新,5 分钟内全国边缘节点回源,实现 秒级回滚。
- 若仅部分 bundle 异常,可把 异常 bundle 的 hash 加入黑名单,后台下发 “强制重新下载列表”,客户端在 RuntimeHashProvider 里拦截,走备用 URL(如
backup-cdn.example.com)重新拉取。
通过以上六层,Addressable 与国产 CDN 回源机制深度耦合,实现 增量更新、断点续传、秒级回滚、灰度发布,且 零侵入 Unity 源码,全部通过 ScriptableBuildPipeline + Custom Provider 完成,符合国内上线规范。
拓展思考
- WebGL 平台 无法使用 DownloadHandlerFile,只能走 indexedDB 缓存,此时可把 bundle 拆成 ≤50 MB 的小文件,利用 HTTP/2 多路复用 并行加载,ServiceWorker 做 离线回退。
- 抖音/微信小游戏 禁止使用 IP 直连 CDN,必须走 厂商域名,需在 首包内嵌 200 KB 的“微资源”,保证 首屏 1.5 s 可交互,后续再 后台静默更新。
- 资源加密 若走 AES-GCM,密钥如何下发?可结合 国内合规的 TEE(支付宝安全容器) 或 微信云托管密钥服务,密钥每小时轮转,客户端用 RSA-OAEP 解密,防止破解版直链。
- CDN 费用优化:阿里云 OSS 回源流量免费,但 流出流量 0.5 元/GB;可把 高频公共资源(Unity 官方 Shader、字体) 提前预热到 边缘节点,低频剧情动画 放 低频 bucket,按 30 天生命周期自动沉降为低频存储,节省 30% 成本。