☰
CDN(内容分发网络):把货提前放到离你最近的仓库
2026/10/17 1:42:55 网站建设 项目流程

一句话:CDN 把网站或游戏的静态文件(图片、安装包、补丁、视频)提前复制到全国乃至全球各地的服务器上,玩家从离自己最近的那台下载,速度更快,源服务器的压力也更小。


一、生活里的例子:电商的"前置仓"

一家网店的总仓库在广州:

  • 没有前置仓:哈尔滨的用户下单,货从广州发,要好几天;"双十一"时总仓库门口的车排成长龙
  • 有前置仓:提前把热门商品运到各城市的小仓库,哈尔滨的用户下单,从本地仓发货,当天到
电商CDN
广州总仓库源站(Origin):你自己的服务器
各城市的前置仓边缘节点(Edge)
热门商品被频繁访问的文件
本地仓有货,直接发缓存命中(Hit)
本地仓没货,从总仓调回源(Miss)
订单系统决定从哪个仓发货调度系统(通常靠 DNS)

二、没有 CDN 会遇到什么问题

假设游戏服务器在上海,发布了一个 2GB 的更新包:

问题原因
远距离延迟高光在光纤里每 1000 公里大约要 5ms(单程),国外玩家往返动辄 150ms+,TCP 下载要来回很多次
源站带宽被挤爆10 万人同时下载 2GB,源站出口带宽根本不够
跨运营商慢服务器在电信,联通、移动的玩家访问要绕路
单点故障源站一挂,所有人都下载失败
容易被攻击DDoS 流量直接打到源站

带宽这个问题最致命:延迟可以忍,下载不动才是真的玩不了。


三、CDN 由哪几部分组成

┌──────────────┐ │ 源站 Origin │ ← 你自己的服务器 / 对象存储(OSS、S3) └──────┬───────┘ │ 回源 ┌────────┴────────┐ │ 中间层(回源保护)│ ← 汇总各地的回源请求,保护源站 └───┬────────┬────┘ ┌────────┘ └────────┐ ┌────┴────┐ ┌────────┐ ┌────┴────┐ │北京节点 │ │广州节点 │ │成都节点 │ ← 边缘节点:直接面对玩家 └────┬────┘ └───┬────┘ └────┬────┘ 玩家 玩家 玩家 调度系统(DNS / Anycast):决定每个玩家连哪个节点
组成部分作用
源站存着文件的"原件",CDN 缓存没有时来这里取
边缘节点分布在各城市、各运营商的缓存服务器,玩家直接从这里下载
中间层多个边缘节点同时缺同一个文件时,只让一个请求去源站(也叫 Origin Shield)
调度系统根据玩家的位置、运营商、节点负载,选出最合适的节点

四、一次请求的完整流程

玩家要下载https://patch.mygame.com/v1.2/data.bundle:

1. 玩家电脑:patch.mygame.com 是哪个 IP? → 问本地 DNS 2. DNS:patch.mygame.com 是一个别名(CNAME) → 指向 patch.mygame.com.cdn-vendor.net 3. CDN 的调度 DNS: 查看"来问的人"在哪:广东、电信 → 返回广州电信节点的 IP:203.0.113.25 4. 玩家连接 203.0.113.25,发起 HTTP 请求 5. 广州节点查缓存: ├─ 有,且没过期 → 直接返回(命中,最快) └─ 没有 / 已过期 → 去中间层或源站取 → 存一份 → 返回给玩家(回源) 6. 下一个广东电信玩家来要同一个文件 → 直接命中

用dig(或 Windows 上的nslookup)可以亲眼看到第 2、3 步:

$ dig patch.mygame.com patch.mygame.com. 300 IN CNAME patch.mygame.com.cdn-vendor.net. patch.mygame.com.cdn-vendor.net. 60 IN A 203.0.113.25

接入 CDN 的关键动作,通常就是在 DNS 里加一条 CNAME。你的代码和 URL 都不用改,这就是"接口不变、实现替换"。
不同地区的人查同一个域名,得到的 IP 不一样 —— 这就是 CDN 的"就近"。

另一种调度方式:Anycast

方式原理特点
DNS 调度(国内常见)DNS 根据来源返回不同 IP灵活,能按运营商细分;但 DNS 有缓存,切换不够快
Anycast(Cloudflare 等)全球很多节点共用同一个 IP,路由器自动把包送到"最近"的那个切换快,天然抗 DDoS;对路由控制要求高

五、缓存规则:存多久?

CDN 节点判断"缓存还能不能用",主要看源站返回的HTTP 响应头。

最重要的头:Cache-Control

Cache-Control: public, max-age=31536000, immutable
写法意思
max-age=600浏览器和 CDN 都可以缓存 600 秒
s-maxage=3600只对 CDN 等共享缓存生效,优先于 max-age
no-cache可以存,但每次用之前都要问源站"变了没有"
no-store完全不许缓存
private只能浏览器存,CDN 不许存(个人数据)
immutable这个文件永远不会变,过期前不用再问

"变了没有"怎么问:304

CDN → 源站: GET /config.json If-None-Match: "abc123" ← 我手上这份的指纹 源站 → CDN: 304 Not Modified ← 没变,继续用你那份(只回一个头,不传内容) 或 200 OK + 新内容 + ETag: "def456" ← 变了,给你新的

不同文件,不同策略

文件类型建议 TTL原因
文件名带哈希的资源包(data_8f3a2c.bundle)1 年内容变了文件名就变,旧名字永远不会改
图片、音频、视频几天~几个月很少变化
版本清单 / 目录文件(version.json、catalog.json)很短或不缓存它决定玩家"要不要更新",必须及时
公告、活动配置1~5 分钟要能较快生效
登录、支付、玩家数据接口不缓存每个人都不一样

六、最重要的工程技巧:用文件名做版本号

文件更新后,CDN 上还缓存着旧的,怎么办?

方法做法问题
刷新(Purge)在 CDN 控制台提交 URL,让各节点删掉缓存全球节点生效要几分钟;有次数限制;浏览器、客户端本地的缓存删不掉
加参数data.bundle?v=2改参数让 URL 变化依赖 CDN 把参数算进缓存键,有的配置会忽略参数
✅文件名带内容哈希data_8f3a2c.bundle内容一变,文件名就变几乎没有问题,业界标准做法

带哈希文件名的好处:

旧版本: version.json → 指向 data_8f3a2c.bundle (缓存 1 年) 新版本: version.json → 指向 data_c91e47.bundle (新文件,第一次访问时回源) ↑ 只有这一个小文件需要"不缓存/短缓存"

原则:大文件"永不变、永久缓存",只让一个很小的入口文件保持新鲜。新旧版本的文件可以同时存在,玩家更新到一半也不会拿到混乱的组合。

还有一招:预热(Prefetch)

大版本发布前,提前让 CDN 把新文件拉到各节点。否则开服那一刻,几十万人同时触发回源,源站会被打垮 —— 这叫缓存击穿 / 回源风暴。


七、怎么衡量 CDN 用得好不好

指标意思目标
命中率请求中直接从节点返回的比例静态资源 > 95%
回源率 / 回源带宽有多少流量打到了源站越低越好
首字节时间(TTFB)发出请求到收到第一个字节命中时通常几十毫秒
下载速度大文件的平均速度按地区、运营商分别看
错误率4xx / 5xx 比例5xx 高通常是源站出问题

命中率低的常见原因:

原因解决
URL 里带了随机参数(?t=时间戳)静态资源不要加随机参数;或配置 CDN 忽略参数
TTL 太短带哈希的文件放心设长
源站返回了no-store/private检查源站响应头
文件太冷门,很少人访问正常现象,可以接受

八、CDN 附带的"额外福利"

功能说明
HTTPS证书部署在边缘节点,加密握手离玩家近,更快
抗 DDoS攻击流量被分散到大量节点吸收,源站 IP 可以隐藏
压缩自动 gzip / brotli 压缩文本(json、js),注意已压缩的资源包别重复压
断点续传支持Range请求,大文件下到一半断了可以接着下
防盗链检查Referer,或使用签名 URL(带过期时间和签名,别人拿到链接也很快失效)
回源 IP 白名单源站只允许 CDN 的 IP 段访问 —— 上一篇子网掩码的/24写法在这里就派上用场

签名 URL 的大致样子:

https://cdn.mygame.com/vip/skin_01.bundle?expires=1735689600&sign=3f9a1c... sign = 哈希(密钥 + 文件路径 + expires)

CDN 节点用同一个密钥算一遍,对得上、没过期才放行。密钥只在服务器端,客户端只拿到已签好的 URL。


九、在游戏里,CDN 用在哪

✅ 适合

内容说明
安装包、大版本补丁流量最大的部分,几乎必须用 CDN
热更新资源(AssetBundle / Addressables)不发新版本也能更新美术、配置、关卡
版本清单、公告、活动配置短 TTL
头像、截图、回放文件用户生成的内容
官网、活动页传统 Web 加速

❌ 不适合

内容原因
实时战斗同步CDN 缓存的是"大家都一样的文件",战斗数据每人每帧都不一样,而且多为 UDP
登录、匹配、支付接口动态请求,不能缓存

实时对战要的是另一类服务:游戏加速网络 / 全球加速(就近接入 + 专线回到游戏服务器)。它借用了 CDN"就近接入"的思路,但不做缓存,只负责走更稳的路线,降低延迟和抖动 —— 也就是减轻上一篇抖动缓冲区的压力。


十、实战:Unity 热更新的 CDN 流程

整体流程

游戏启动 │ ▼ 1. 下载 version.json(不缓存,或 TTL 很短) { "version": "1.2.3", "catalog": "catalog_c91e47.json" } │ ▼ 2. 和本地版本对比 → 一样?直接进游戏 │ 不一样 ▼ 3. 下载新的 catalog(文件名带哈希,可长期缓存) │ ▼ 4. 对比哪些资源包变了 → 只下载变化的(data_xxxx.bundle) │ ▼ 5. 校验文件哈希 → 全部通过 → 替换本地版本号 → 进游戏

Addressables 的关键设置

设置推荐
Remote Load Pathhttps://cdn.mygame.com/[BuildTarget]/
Bundle Naming ModeAppend Hash(文件名带哈希)
Catalog开启Build Remote Catalog;catalog 的.hash小文件配短 TTL
CDN 规则.bundle长缓存;.hash/version.json短缓存或不缓存

下载版本清单(简化示例)

usingSystem.Collections;usingUnityEngine;usingUnityEngine.Networking;publicclassVersionChecker:MonoBehaviour{conststringVersionUrl="https://cdn.mygame.com/android/version.json";[System.Serializable]classVersionInfo{publicstringversion;publicstringcatalog;}IEnumeratorStart(){usingvarreq=UnityWebRequest.Get(VersionUrl);// 告诉沿途缓存:要最新的(CDN 侧也应为这个文件配置短 TTL,双保险)req.SetRequestHeader("Cache-Control","no-cache");req.timeout=10;yieldreturnreq.SendWebRequest();if(req.result!=UnityWebRequest.Result.Success){Debug.LogWarning($"版本检查失败:{req.error},使用本地资源进入");yieldbreak;// 网络问题不应该让玩家完全进不了游戏}varinfo=JsonUtility.FromJson<VersionInfo>(req.downloadHandler.text);stringlocal=PlayerPrefs.GetString("res_version","0");if(info.version!=local){Debug.Log($"发现新版本{info.version},开始下载{info.catalog}");// 接着:下载 catalog → 计算差异 → 下载变化的 bundle → 校验哈希// Addressables 中可用 CheckForCatalogUpdates / UpdateCatalogs / DownloadDependenciesAsync}}}

注意:这里没有在 URL 后面加?t=时间戳来强制刷新。那样每次都会回源,CDN 命中率为零。正确做法是在 CDN 上给这个文件配短 TTL。

大版本日的流量规划

措施作用
提前预热新资源避免开服瞬间回源风暴
分批推送更新提示不让所有人在同一分钟下载
多家 CDN 互备一家出故障,客户端切到备用域名
客户端失败重试 + 断点续传下到 90% 断网不用从头来
下载前检查剩余空间避免下完才发现装不下

十一、和前面几篇的联系

前面讲过的关系
网关玩家的请求先经过家里网关出去;CDN 节点通常部署在运营商机房,离网关只有几跳
子网掩码CDN 按 IP 段判断用户在哪个地区、哪个运营商;源站白名单也按网段配置
抖动缓冲区在线视频的"缓冲中"就是 CDN 下载 + 抖动缓冲共同作用;CDN 越近越稳,缓冲就可以越小
系统调用高性能 CDN 节点用sendfile等零拷贝调用,文件直接从内核发到网卡,不进用户态
架构原则静态(CDN)与动态(游戏服务器)分离,各自独立扩容

十二、常见的坑

坑现象解决
版本清单被长时间缓存发了新版本,玩家一直检测不到更新入口文件配短 TTL 或不缓存
资源包文件名不变,只改内容有人拿到新的、有人拿到旧的,资源错乱文件名带内容哈希
更新时先上传 catalog,后上传 bundle玩家读到新目录,却下载不到新文件,404先传资源包,最后传入口文件
每次请求都加随机参数命中率接近 0,回源费用暴涨静态资源不加随机参数
把个人数据接口接入 CDNA 玩家看到了 B 玩家的数据动态接口返回private/no-store,或不走 CDN
404 也被缓存了文件补传后,仍然一直 404缩短错误响应的缓存时间;先传文件再公开
只刷新了 CDN部分玩家仍是旧内容客户端/浏览器本地也有缓存,靠文件名版本号解决
源站没限制访问别人绕过 CDN 直接打源站源站只放行 CDN 回源 IP 段,或校验回源密钥头
发布日没预热开服瞬间源站被打挂提前预热 + 中间层 + 分批推送
忘了海外玩家国内飞快,海外极慢选择覆盖目标地区的 CDN,或多 CDN 按地区调度
不看账单月底流量费吓一跳设置流量告警;压缩资源;提高命中率

一句话总结

CDN = 源站 + 遍布各地的缓存节点 + 就近调度。玩家通过 DNS(CNAME)或 Anycast 被分配到最近的节点:有缓存就直接返回,没有就回源取一份再返回。
用好 CDN 的关键只有一条:大文件用"内容哈希命名 + 长期缓存",只让很小的入口文件保持新鲜。
在游戏里,它负责安装包、补丁、热更新资源;而实时战斗流量需要的是加速网络,不是缓存。


需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询