跳到主要内容

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-EncodingAccept-Language
  • 是否含 cookie:登录态资源(一般不缓存)

4.2 缓存策略来源(优先级从高到低)

  1. CDN 控制台配置规则(强制覆盖)
  2. 源站响应头 Cache-Control / Expires
  3. 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@EdgeAWS 生态
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.jscdn2/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 必须 privateno-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. 延伸阅读