对象存储、CDN 与资源分发
Category
database / cache 子树。节点定位:对象存储、CDN 与资源分发——围绕「让绝大多数读取不回到源站」这一个目标展开寻址、版本、权限与刷新治理。
Definition
客户端游戏的安装包、手游的热更文件、启动器与官网的静态资源,都属于同一类问题:大量不常变化的文件,要被海量用户快速、便宜、稳定地拉取。对象存储负责把文件放好,CDN 负责把文件搬到离用户近的地方。这一层平时几乎没有存在感,可一旦版本管理混乱或缓存策略失控,损失往往以“整次发布失败”或“带宽账单翻倍”的形态出现。
Problem
资源分发追求高吞吐低成本,与业务接口的低延迟强一致撑不起同一套架构;它的错误以「多少人更新失败」计,损失常以整次发布失败或带宽账单翻倍出现。覆盖旧路径发布新版本,客户端、CDN 与源站三方内容长期不一致;把缓存刷新当常规发布步骤,回滚时会被边缘节点的旧内容卡住。
资源分发为什么是独立的一层问题
业务接口追求低延迟和强一致,资源分发追求高吞吐和低成本,两套目标撑不起同一套架构。接口走数据库和缓存,资源走对象存储和边缘节点;接口的错误以个位数失败请求计,资源分发的错误以“多少人更新失败”计。
这层问题的特殊之处在于:
- 文件大、数量多,单个文件的获取成本不能和接口请求放在同一量级讨论。
- 读极多写极少,写入集中在发布时刻,读取却延续到发布之后的漫长时间里。
- 用户分布广,源站带宽和跨网流量成本远高于业务接口流量。
所以资源分发的设计几乎都围绕一个目标:让绝大多数读取不回到源站。所有后续讨论——寻址、版本、刷新——都是这个目标在不同环节的投影。
内容寻址:让文件名自己携带版本
对象存储里的文件按什么组织,直接决定版本管理的难易。把新版本覆盖写到旧路径上是最省事也最危险的做法——所有引用这个路径的客户端,拿到的到底是新文件还是旧文件,取决于途中哪一层缓存还没过期,谁也说不清。
更稳妥的常见做法是内容寻址,让新版本天然对应新路径:
- 按内容哈希命名,内容变则路径变,文件天然不可变。
- 按版本号或构建号组织目录,一次发布对应一组新对象。
- 清单文件单独管理,客户端先取清单,再按清单里的路径取文件。
不可变路径的价值在于把“更新”从覆盖改写变成了发布:新旧版本同时存在,出问题随时切回去,途中任何一层缓存都不需要被“说服”。这份不可变性是后面所有治理手段的地基。
版本治理:回滚靠切换指针而不是重新上传
发布的本质不是“把新文件传上去”,而是“把某个版本声明为当前版本”。这两件事分开做,回滚就是一次指针切换,秒级完成;混在一起做,回滚就是一次重新上传,外加一段不可控的缓存等待。
版本治理通常会形成几条纪律:
- 每次发布的产物完整保留,而不是原地覆盖。
- 当前版本用一个独立的、小而常变的清单文件声明。
- 客户端兼容多个版本,旧版本的下线有明确的时间窗口。
- 差量包和全量包都提前生成,并经过校验。
游戏还有一个额外维度:渠道包。同一个版本对不同渠道往往要打不同的包,版本号、渠道号、构建产物三者如果对不上,出问题时连“线上到底跑的是哪个版本”都难以回答。版本治理是否合格,看的就是这种时刻能不能在几分钟内给出答案。
权限与防盗链:分发不等于公开
游戏资源并非都该公开:付费资源、测试包、内嵌活动素材都需要访问控制。对象存储通常支持私有读写加临时授权,CDN 则常配合校验参数和域名控制做限制。
常见的控制手段与各自适用面:
- 签名链接加过期时间,适合测试包和付费内容的临时分发。
- 校验参数,拦截最外层的直接盗链,成本最低,但挡不住已经拿到链接的人。
- 来源与域名白名单,能挡住粗放的站外引用,伪造也容易,只当第一道过滤。
值得想清楚的一点是:资源一旦被客户端下载,本地就有副本,分发层的任何手段都拦不住主动破解。防盗链的目标是把随意的、大规模的盗用变贵,而不是做到严格保密——按这个预期来配置手段的强度即可,过重的校验反而会伤到正常玩家的更新体验。
缓存刷新往往靠不住
CDN 的价值是把文件缓存在边缘节点,代价是“让缓存消失”这件事脱离了自己的控制。覆盖源站文件之后发起刷新,通常只是一次提示:部分节点更新了,部分节点因为仍命中本地缓存而保持旧内容,各节点的生效时间也参差不齐。
依赖刷新的发布链路,问题会在这些时刻集中爆发:
- 灰度想按用户切流,缓存却按文件维度生效,两条曲线对不上。
- 出问题要回滚,回滚包推上去了,边缘节点还在吐旧文件。
- 客户端自己还有一层本地缓存,服务端刷新成功也不代表用户拿到新版。
所以成熟团队的取向通常是反过来:默认不刷新,让路径不可变,更新就是发布新路径。刷新能力保留着,但只当应急工具,不当常规发布步骤。任何“改了旧文件然后等缓存过期”的方案,最终都会在某个版本变成排障噩梦。
多端、多渠道与热更的组合成本
资源分发的复杂度往往不在单个文件,而在组合:多语言、多机型、多渠道、多版本同时在线。清单和差量机制要支撑的是“每个客户端算出自己的那一组文件”,而不是“所有人下载同一个包”。
通常值得提前回答的问题:
- 一次热更从发布到大多数玩家生效要多久,弱网和存储不足的设备怎么办。
- 差量包的基线版本保留多少个,太少了差量退化成全量,太多了存储和管理成本上升。
- 客户端校验失败的兜底路径是什么,能否回退到上一个可用版本。
这些问题的答案共同决定了“发布之后还要多久才算发布完成”。分发层的第一手观察指标也在这里:各版本玩家的分布、热更成功率、差量命中率,比带宽曲线更能预示即将到来的客服压力。
常见误区
- 覆盖旧路径发布新版本,客户端、CDN 与源站三方看到的内容长期不一致。
- 把缓存刷新当发布流程的常规步骤,回滚时被边缘节点的旧内容卡住。
- 历史版本产物被清理或覆盖,出了问题连复现当时现象的文件都找不到。
- 防盗链配置完就认为资源安全,忽略客户端本地的副本才是真正的边界。
- 只看带宽成本,不监控各版本玩家分布与热更成功率,问题到客服侧才被发现。
资源分发这一层的判断核心,是把发布理解为声明版本而不是修改文件,把刷新理解为应急手段而不是依赖路径。
Used By
资源分发体系设计(内容寻址、清单与差量、指针切换回滚);发布与回滚流程纪律的制定;防盗链强度预期;分发指标选型(版本分布、热更成功率、差量命中率)。
Related
关系链:分发目标(读不回源)→ 内容寻址(路径不可变)→ 版本指针(回滚=切换)→ 防盗链预期 → 刷新只做应急 → 多端组合治理。上游:production 树的版本发布(production/03)。下游:client 树的热更与资源管线(client/03)。
Reference
本页为工程经验归纳;各对象存储与 CDN 的能力细节以官方文档为准。