CDN工作原理与调度策略
1. 概念与原理
CDN(Content Delivery Network)= 把内容缓存到离用户最近的节点。前端项目里 90% 的静态资源(JS、CSS、图片、视频、字体)都该走 CDN。
核心解决两个问题:
- 延迟:物理距离决定 RTT 下限。北京用户访问美西服务器 200ms+,访问北京 CDN 节点 10ms 内
- 带宽:源站带宽贵且有限,CDN 把流量分摊到全球节点
2. CDN 架构
(源站 Origin)
↑
│ 回源(cache miss)
│
┌──────────────┼──────────────┐
│ │ │
[L2 节点] [L2 节点] [L2 节点] ← 中间层(区域中心)
│ │ │
┌──┴──┐ ┌──┴──┐ ┌──┴──┐
│边缘 │ │边缘 │ │边缘 │ ← Edge POP(用户最近)
│ POP │ │ POP │ │ POP │
└──┬──┘ └──┬──┘ └──┬──┘
│ │ │
用户 用户 用户
层级:用户 → 边缘节点 → 区域中心 → 源站。每层都缓存。
3. 调度机制(用户怎么连到最近节点)
3.1 DNS 调度(最常见)
1. 用户访问 www.example.com
2. 域名 CNAME → www.example.com.cdn.com
3. 用户本地 DNS 查 cdn.com 权威 DNS
4. CDN 权威 DNS 看请求方 IP(含 EDNS Client Subnet)
5. 返回最近节点 IP
6. 用户连节点
优点:成熟、改造小。 缺点:
- 依赖本地 DNS 位置:若用户用国外 DNS(如 8.8.8.8),EDNS 没透传时会被分配到错地区
- TTL 制约切换速度
3.2 Anycast
同一 IP 在全球多个节点宣告 BGP 路由,运营商路由协议自动让流量走最近节点。Cloudflare、Fastly 大量用。
优点:
- 无 DNS 延迟,连接直接到最近节点
- 故障切换瞬时(节点下线 BGP 撤销)
缺点:要求 CDN 厂商有 BGP 能力。
3.3 HTTP 302 调度
源站收到请求后返回 302 让客户端重新访问最优节点。延迟高,少用。
3.4 客户端 SDK 调度
APP 侧 SDK 测速选最快节点。视频、直播常用。
4. 缓存机制
4.1 Cache Key
CDN 判断"两个请求是同一资源"的标识。默认是 URL(含 query string),但可配置:
- 是否含 query:图片处理参数
?w=200是不同资源 - 是否含某些 header(Vary):
Accept-Encoding、Accept-Language - 是否含 cookie:登录态资源(一般不缓存)
4.2 缓存策略来源(优先级从高到低)
- CDN 控制台配置规则(强制覆盖)
- 源站响应头
Cache-Control/Expires - CDN 默认策略
4.3 关键响应头
Cache-Control: public, max-age=31536000, immutable # 静态资源 hash 文件名
Cache-Control: public, max-age=300, s-maxage=3600 # CDN 缓存 1h、浏览器 5min
Cache-Control: no-cache # 协商缓存
Cache-Control: no-store # 完全不缓存
Cache-Control: private # 只浏览器缓存,CDN 不缓存
s-maxage 是 CDN 专用。
4.4 Vary 头
告诉缓存"按这些头分别缓存":
Vary: Accept-Encoding # gzip 和 br 版本分别缓存
Vary: User-Agent # 不同浏览器分别缓存(慎用,命中率极低)
Vary: User-Agent 是缓存杀手,CDN 几乎不命中。
5. 回源与刷新
5.1 回源(Cache Miss)
边缘节点没缓存时回源站取。流程:
用户 → 边缘 POP(miss)
→ L2 中间层(miss)
→ 源站(hit)
← 200 + Cache-Control
← 缓存 + 返回
← 缓存 + 返回
回源量大 = 源站压力大 = CDN 没起作用。监控 回源率 = 回源带宽 / 总带宽,健康站点应 < 5%。
5.2 回源拉取失败
源站挂了 / 5xx,CDN 默认会返回错误给用户。生产配置应开故障回源:
- stale-while-revalidate:缓存过期后先返回旧内容,后台异步更新
- stale-if-error:源站错时返回旧缓存
- 自定义错误页
5.3 刷新(Purge)
发版后需要立即让用户拿到新版本。两种方式:
- URL 刷新:精确刷某个 URL(生效快,1-3 分钟)
- 目录刷新:刷某个路径前缀(慢,10-30 分钟,配额少)
- 变量名缓存(Cache Tag):给资源打标签,按标签刷(Cloudflare、Fastly 支持)
前端最佳实践:所有静态资源带 hash 文件名(app.abc123.js),CDN 缓存 1 年 + immutable。发版只刷 index.html(无 hash)。
5.4 预热(Prefetch)
发版后主动让 CDN 节点拉取新资源,避免首批用户回源。配额比刷新少。
6. CDN 节点信息
响应头能看出 CDN 状态:
X-Cache: HIT # 命中边缘
X-Cache: MISS # 没命中
X-Cache: HIT from L1, MISS from L2
Age: 3600 # 已缓存秒数
X-Served-By: cache-bj-001
Server: AliCDN
CF-Cache-Status: HIT # Cloudflare 专用
CF-Ray: 12345abc-HKG # Cloudflare 请求 ID + 命中节点
7. 主流 CDN 对比
| 厂商 | 特点 | 适合 |
|---|---|---|
| Cloudflare | 免费版强、Anycast、安全产品多 | 海外、个人/小团队 |
| Fastly | 实时刷新、VCL 强可编程 | 头部互联网(GitHub、Shopify) |
| 阿里云 CDN | 国内节点多、价格便宜 | 国内业务 |
| 腾讯云 CDN | 类似阿里 | 国内业务 |
| AWS CloudFront | 集成 AWS 生态、Lambda@Edge | AWS 生态 |
| Akamai | 老牌、企业级 | 大企业、严苛合规 |
7.1 国内 ICP 备案
国内 CDN 节点要求源站和域名都备过案。没备案只能用境外节点(速度差)或绕道香港。
7.2 多 CDN 容灾
大型业务用 2-3 家 CDN 互备,DNS 智能切换(按可用性、性能)。开源方案 Cedexis 类。
8. 边缘计算
新一代 CDN 支持在边缘节点运行代码(V8 isolate、WASM):
- Cloudflare Workers:JS / TS,全球边缘
- Fastly Compute@Edge:WASM
- AWS Lambda@Edge:Node / Python
- 阿里 ER(Edge Routine)
适合的场景:
- A/B 测试(按 cookie 分组改写响应)
- 简单 API 聚合
- 鉴权预处理
- 图片处理 / 重定向
- 边缘渲染(SSR at edge)
详见模块 07。
9. 性能优化
9.1 提升命中率
- 资源带 hash + 长缓存(max-age=31536000, immutable)
- 减少 query 参数差异
- 避免
Vary: User-Agent - 同一资源用同一 URL(不要
cdn1/x.js、cdn2/x.js)
9.2 节省回源
- 开启回源压缩(CDN 到源站 gzip,省源站带宽)
- 设置合理的 stale-while-revalidate
- 大文件分片缓存(Range 请求)
9.3 Brotli 比 gzip 小 15-25%
CDN 控制台开 Brotli。
9.4 HTTP/2 / HTTP/3
CDN 一般默认开。看响应 protocol 字段确认。
9.5 图片优化
- 智能 WebP/AVIF 转换(CDN 看
Accept自动转) - 按设备分辨率裁剪(
?w=375) - 懒加载
9.6 移动端动态加速
CDN 厂商对动态请求(API)有"动态加速"产品:BGP 多线路 + TCP 优化(BBR、连接预建立)+ 私有协议回源。比直连快 30-50%。
10. 故障排查
10.1 CDN 没命中
# 看响应头
curl -I https://cdn.example.com/app.js | grep -i "cache\|age"
# 第一次 MISS 正常,连续 MISS 排查:
# 1. URL 是否每次不同(带时间戳?随机参数?)
# 2. Cache-Control 是否 no-store
# 3. 是否 Vary: Cookie / UA
# 4. CDN 控制台规则是否设了"不缓存"
10.2 刷新后用户还看到旧版本
# 1. 浏览器缓存:用户没强刷
# 2. CDN 部分节点未刷到(罕见,但区域节点刷新有延迟)
# 3. 服务端 worker 缓存(Service Worker)
# 4. ISP 透明缓存(运营商):用 HTTPS 可绕过
10.3 海外用户慢
- 看 CDN 节点分布是否覆盖(厂商节点图)
- 是否被分配到错节点(地区 DNS / EDNS 问题)
- 跨境回源(节点没缓存还要跨海回源)
10.4 跨域问题
CORS 必须在响应头:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Authorization, Content-Type
Vary: Origin # 多 Origin 时必加,否则缓存串
Vary: Origin 漏写是经典坑:第一个用户的 Origin 被缓存,后续不同 Origin 都拿到第一个的响应头。
10.5 节点故障定位
# 看分配的节点 IP
dig cdn.example.com +short
# ping 看延迟
ping <node-ip>
# 看响应头里的节点 ID
curl -I https://cdn.example.com/x.js | grep -i "x-served-by\|cf-ray"
# 多地区测速
# https://www.17ce.com 国内
# https://www.webpagetest.org 全球
11. 安全考量
- Token Auth / Signed URL:付费内容用签名 URL,过期失效,防盗链
- Referer 防盗链:CDN 控制台配,挡基础盗刷
- HTTPS 强制:CDN 上配置 HTTP → HTTPS 301
- WAF:CDN 自带 WAF(Cloudflare、阿里)阻 SQL 注入、XSS
- DDoS:CDN 是天然防 DDoS(流量分散),但 7 层攻击(应用层)需 WAF + 限速
- 缓存投毒:恶意构造 query 让 CDN 缓存攻击 payload。规范化 cache key + 限制可缓存方法
- 私有信息不能缓存:含 user-id 的 API 必须
private或no-store - Origin Shield:CDN 厂商提供的回源屏蔽层,源站 IP 不暴露 + 进一步分摊回源
12. 常见反模式
- JS / CSS 不带 hash 文件名:缓存不敢长,每次发版要刷 CDN(限额 + 慢)
- 同一资源用不同 URL:缓存失效率高
Cache-Control: no-cache当不缓存用:实际是协商缓存Vary: User-Agent:缓存命中率几乎为 0- CORS 漏
Vary: Origin:响应头串污染 - API 走静态 CDN:默认缓存策略适配静态资源,API 命中错缓存导致数据串
- 不监控回源率:源站默默扛压,CDN 钱白花
- 不开 Brotli:白白多用 20% 带宽
- 图片回源 + 大尺寸:用户看个头像 CDN 回源 5MB 原图
- Service Worker 缓存策略和 CDN 冲突:发版后用户拿不到新版本
- 没有 CDN 故障预案:CDN 全挂时直接源站扛流量必然倒
13. 延伸阅读
- 《CDN 技术详解》雷葆华 — 国内 CDN 系统性书
- Cloudflare Learning: CDN
- Fastly Blog — 大量边缘计算实践
- 阿里云 CDN 文档
- HTTP Cache RFC 9111
- Edge Computing 综述(IEEE)