Skip to content

Stage03|Runtime Objects & Assets ​

Lesson034|Asset Bundle、远程资源、CDN 与版本管理 ​

Tags: #Creator2.x #Stage03 #AssetBundle #RemoteAsset #CDNDifficulty: ⭐⭐⭐⭐☆


真实问题:CDN 已上传新活动,玩家为什么仍看到旧界面 ​

运营把 summer Bundle 的新文件覆盖到 CDN。开发机清缓存后正常,线上玩家却分成三类:

  • 有人看到新活动;
  • 有人仍看到旧图片;
  • 有人 Bundle 配置是新的,但依赖图片 404。

这不是简单的“CDN 缓存没刷新”。远程 Bundle 是一条带版本的发布协议:

text
客户端内置入口
→ Bundle 版本/配置
→ CDN URL
→ 配置与资源文件
→ 本地下载缓存
→ Asset 依赖解析
→ 业务实例化

任一层新旧不一致,都可能形成混合版本。

一、本课目标 ​

随着资源量增长,不能把所有资源都和首包一起加载。本课理解:

  • Asset Bundle 的组织价值。
  • 远程资源的加载边界。
  • CDN 和版本清单的作用。
  • 为什么资源更新必须考虑缓存和回滚。

二、Bundle 解决什么问题 ​

可以把 Bundle 理解成可独立管理的一组运行时资源:

text
首包
├── 基础 UI
└── 启动场景

Bundle: level_01
Bundle: activity_summer
Bundle: character_skin

它可以帮助:

  • 延迟加载低频内容。
  • 拆分首包和更新包。
  • 按功能组织依赖。
  • 控制资源下载和释放边界。

具体 API 和构建配置以 Creator 2.4.x 项目为准,不要把 3.x Bundle API 直接套用到 2.x。


三、远程资源的完整链路 ​

text
客户端请求资源地址
    ↓
CDN / 文件服务器
    ↓
下载资源和依赖
    ↓
校验版本或哈希
    ↓
本地缓存
    ↓
运行时 Loader 解析
    ↓
业务使用

失败点比本地资源多:

  • 网络不可用。
  • CDN 节点异常。
  • 版本清单不一致。
  • 下载中断。
  • 本地缓存损坏。
  • 依赖资源版本不匹配。

四、版本清单为什么重要 ​

资源发布通常需要一个版本描述:

text
资源名
版本号
文件哈希
大小
依赖版本
下载地址

客户端通过清单判断:

text
本地是否已有
是否需要更新
缓存是否完整
依赖是否兼容

只改文件名或时间戳而没有一致性校验,容易让客户端拿到新旧资源混合的状态。


五、缓存和 CDN 的关系 ​

text
客户端缓存
    ↓ 未命中
CDN 边缘节点
    ↓ 未命中
源站

发布新资源时要考虑:

  • URL 是否带版本号或内容哈希。
  • CDN 缓存多久刷新。
  • 旧版本客户端能否继续访问旧资源。
  • 回滚时旧资源是否仍保留。

资源更新不是“上传文件”这么简单,而是客户端、缓存和服务端的协议。


六、远程资源的生命周期 ​

text
未下载
→ 下载中
→ 本地缓存
→ 解码加载
→ 页面使用
→ 页面关闭
→ 仍可能被其他页面使用
→ 安全释放

远程下载完成不等于 GPU 纹理已经准备完成;本地缓存文件存在也不等于运行时对象不会再次解码。


七、案例:活动资源包 ​

text
主包:基础 UI、登录、大厅
活动 Bundle:活动场景、图集、音频、配置

进入活动:

text
读取活动版本清单
→ 检查本地缓存
→ 下载缺失依赖
→ 加载活动 Scene
→ 显示活动页面

退出活动:

text
关闭页面
→ 取消监听和异步回调
→ 解除对象引用
→ 按使用关系释放资源

八、常见误区 ​

误区一:Bundle 就是一个压缩 ZIP ​

它更重要的是资源组织、依赖和加载边界,具体产物由构建系统决定。

误区二:CDN 更新文件后所有客户端立即拿到新版本 ​

本地和 CDN 缓存可能继续返回旧资源。

误区三:下载成功就代表资源可使用 ​

还可能需要校验、解码、依赖加载和运行时对象创建。

误区四:远程资源不需要释放 ​

下载和运行时缓存都可能占用磁盘或内存。


九、练习与答案 ​

练习 ​

  1. Bundle 的主要价值是什么?
  2. 远程资源链路比本地资源多哪些失败点?
  3. 版本清单需要描述什么?
  4. 为什么 URL 版本化有助于缓存更新?

参考答案 ​

  1. 按功能拆分资源,支持延迟加载、更新和释放边界。
  2. 网络、CDN、版本、下载完整性、缓存和依赖兼容问题。
  3. 版本、哈希、大小、地址和依赖信息。
  4. 新 URL 避免旧缓存继续命中同一个地址。

Bundle 的三个身份不要混淆 ​

业务模块身份 ​

例如 summer-event,表达活动模块。

构建版本身份 ​

构建输出中的 hash、版本参数或清单,帮助区分内容版本。

远程地址 ​

CDN 上实际提供配置和资源的 URL。

错误发布常把“相同 Bundle 名”当成“相同内容”。客户端需要拿到一组内部一致的配置与资源,不能让入口清单指向新配置,而新配置的一部分依赖仍未上传。

Creator 2.4.x 本地实验:先验证 Bundle 边界 ​

Bundle 配置、资源移动和构建通过 Creator 编辑器完成。不要手工伪造 .meta 或直接修补 build/。

准备 ​

  1. 在 Creator 中创建一个 bundle-lab 资源目录。
  2. 通过 Inspector 将它配置为 Asset Bundle。
  3. 放入一个 Prefab 和一张由 Prefab 引用的图片。
  4. 创建入口场景,但不要静态引用该 Prefab。
  5. 创建 BundleProbe.ts。
ts
const { ccclass, property } = cc._decorator;

@ccclass
export default class BundleProbe extends cc.Component {
    @property(cc.Node)
    container: cc.Node = null;

    private generation = 0;

    openBundlePanel() {
        const id = ++this.generation;

        cc.assetManager.loadBundle(
            'bundle-lab',
            (bundleErr, bundle) => {
                if (bundleErr || id !== this.generation) {
                    cc.error('[bundle] load bundle failed', bundleErr);
                    return;
                }

                bundle.load(
                    'panel',
                    cc.Prefab,
                    (assetErr, prefab: cc.Prefab) => {
                        if (assetErr
                            || id !== this.generation
                            || !cc.isValid(this.node)) {
                            cc.error('[bundle] load asset failed', assetErr);
                            return;
                        }

                        this.container.addChild(cc.instantiate(prefab));
                        cc.log('[bundle] panel ready');
                    }
                );
            }
        );
    }

    closePanel() {
        this.generation++;
        this.container.removeAllChildren();
    }
}

实验 A:入口与内部资源分开失败 ​

先故意写错 Bundle 名,再写对 Bundle 名但写错内部路径。

预期得到两类不同错误:

text
loadBundle 失败:入口、名称、位置或配置问题
bundle.load 失败:Bundle 已存在,但内部路径/类型/依赖有问题

诊断日志必须保留这两个阶段,不能统一显示“资源失败”。

实验 B:关闭页面不等于取消共享下载 ​

打开后立即关闭,generation 使回调失效。观察 Bundle/Asset 底层任务仍可能完成。这与 L031 的竞态模型一致。

实验 C:构建验证 ​

通过 Creator Build 面板构建一个测试目标,检查构建日志与 Bundle 输出是否存在。不要手改输出内容;修改源配置后重新构建。

远程发布必须具有原子性 ​

理想发布顺序:

text
1. 生成不可变版本目录
2. 上传全部配置与资源
3. 从外部网络校验每个文件
4. 校验 hash、大小和依赖完整性
5. 最后发布指向新版本的入口
6. 监控错误率
7. 保留旧版本供回滚

不要先覆盖“latest”配置,再慢慢上传依赖。那会产生一个客户端必然能观察到的不完整窗口。

不可变 URL 示例:

text
cdn.example.com/bundles/summer/2026.08.16/...

版本入口只负责选择哪一目录。旧客户端继续访问旧目录,新客户端访问新目录,CDN 缓存不会把不同内容混在同一 URL 下。

缓存要回答三个问题 ​

缓存键是什么 ​

只用 Bundle 名,还是包含版本/hash?

谁判定过期 ​

客户端清单、HTTP Cache-Control、CDN 刷新还是业务版本?

回滚怎样发生 ​

入口切回旧版本后,客户端是否还能访问旧资源?本地是否错误复用了新配置?

“让用户清缓存”不能作为正式版本策略。

真实项目故障:配置成功但图片 404 ​

事故链 ​

text
新 config.json 先发布
→ 配置引用 texture.abcd123
→ 图片文件仍在上传
→ 一部分边缘节点已缓存新配置
→ 客户端请求尚不存在的新图片
→ 页面半新半旧

诊断证据 ​

  • 客户端 Bundle 名与版本;
  • config URL、状态码和响应 hash;
  • 失败资源的完整逻辑路径与最终 URL;
  • CDN 请求 ID/边缘节点;
  • 本地缓存命中情况;
  • 发布流水线各文件到达时间。

修复 ​

先上传不可变资源并校验,最后切入口;失败时入口回滚。资源版本目录在支持周期内不可删除。

安全与可靠性边界 ​

远程资源不仅有可用性问题,还要考虑:

  • HTTPS 与证书;
  • 配置/文件完整性校验;
  • 非法路径和内容注入;
  • 下载大小上限;
  • 超时、重试和退避;
  • 磁盘缓存预算;
  • 蜂窝网络策略;
  • 最低客户端兼容版本。

重试必须有上限和退避,不能在 update 中持续发请求。

版本边界与官方入口 ​

本课以 Creator 2.4.x Asset Bundle 为主。Bundle 构建选项、远程地址和缓存行为应以当前 2.4.x 小版本及目标平台文档为准。

深化练习与答案 ​

  1. 为什么同一 URL 直接覆盖文件容易产生旧内容? 浏览器、本地和 CDN 都可能缓存该 URL;若内容可变,版本身份与缓存键不一致。
  2. 为什么入口文件应最后发布? 入口一旦指向新版本,客户端就可能请求所有新依赖;最后发布能保证被引用内容已经完整可用。
  3. Bundle 下载成功后为什么 Prefab 仍可能失败? Bundle 配置可用不代表内部路径、类型和所有依赖都可用,必须分阶段处理错误。
  4. 回滚为什么要求保留旧版本目录? 入口切回只能选择旧版本,若旧资源已删除,回滚只是把客户端指向另一组 404。

十、本课总结 ​

text
Bundle 管理资源集合和加载边界。
远程资源需要清单、缓存、校验和回滚策略。
CDN 是分发链路,不是版本管理本身。
下载、解码、运行时加载和资源释放是不同阶段。

十一、下一课预告 ​

text
Lesson035|为什么 destroy 节点不等于释放资源?