在 HTTP协议演进 中,看到 HTTP/2 和 HTTP/3 通过多路复用、头部压缩、0-RTT 等机制不断压缩协议层面的开销。但无论协议多精巧,物理距离的延迟无法消除——光速限制下,北京到纽约的往返至少需要 130ms。如果每个请求都要跨越半个地球去源站获取数据,再精巧的协议也救不了用户体验。
CDN(Content Delivery Network)的思路很简单:把数据搬到离用户更近的地方。用户请求不再穿越半个地球去源站,而是被路由到最近的边缘节点,从本地缓存获取内容。这不仅是延迟的优化,更是可靠性、带宽成本和安全防护的系统性解决方案。
本章从 CDN 的设计动机出发,深入任播路由与 PoP 分布的架构原理,解析缓存策略与回源机制的工程细节,探讨动态加速和安全防护的实现方式,最后通过动手实验观察 CDN 的真实行为。下一章 数据中心与SDN网络 将走进 CDN 节点背后的数据中心,看 CLOS 架构和 SDN 如何支撑海量请求的转发。
一、为什么需要CDN
1.1 源站远在天边
互联网的核心设计原则是端到端——数据包从源到宿,中间经过多少跳路由器、穿越多少个 AS,全凭 BGP与域间路由 的策略决定。一个部署在美东的源站,服务亚太用户时,数据包要穿越太平洋海底光缆,经过多个 IXP与互联网交换 点,跳过十几个路由器。
以北京用户访问美东源站为例:
- 物理距离约 11000km,光速限制下单程约 37ms,往返 74ms
- 加上路由器处理、排队、拥塞控制等开销,实际 RTT 通常 150-300ms
- TCP流控与拥塞控制 的慢启动机制意味着前几个 RTT 内带宽利用率极低
- 一次完整的页面加载涉及数十个请求,串行依赖下延迟不断叠加
1.2 单点故障与带宽瓶颈
源站集中部署带来三个工程问题:
单点故障:源站所在机房断电、光纤被挖断、上游路由故障——所有用户同时无法访问。2017 年 Amazon S3 us-east-1 区域故障,导致大量依赖 S3 的网站和服务瘫痪数小时。
带宽瓶颈:热门内容突发流量时,源站出口带宽成为瓶颈。一个 10Gbps 的源站出口,面对全球用户的同时请求,分到每个用户的带宽可能只有几十 Kbps。
成本压力:跨运营商、跨大洲的流量费用远高于本地流量。源站在美东,亚太用户的流量要穿越太平洋,带宽成本是本地流量的 5-10 倍。
1.3 CDN的核心思路
CDN 用”空间换时间”的策略解决上述问题:
| 问题 | CDN 策略 | 效果 |
|---|---|---|
| 物理距离远 | 边缘节点就近响应 | RTT 从 200ms 降到 10-30ms |
| 单点故障 | 多节点冗余 | 单节点故障不影响全局 |
| 带宽瓶颈 | 流量分散到边缘 | 源站带宽需求降低 90%+ |
| 流量成本 | 本地出口 | 跨洲流量减少 80%+ |
二、CDN架构与任播路由
2.1 CDN整体架构
CDN 由分布在全球的 PoP(Point of Presence)节点、调度系统和源站组成:
用户访问 CDN 资源的典型流程:
- 用户请求
cdn.example.com,本地 DNS 递归查询 - 权威 DNS 返回 CNAME,指向 CDN 的全局负载均衡(GSLB)域名
- GSLB 根据用户 IP、节点负载、网络状况返回最优 PoP 的 IP
- 用户向 PoP 发起 HTTP 请求
- PoP 缓存命中则直接返回;未命中则回源获取并缓存
2.2 任播:同一个IP,多个位置
CDN 能让用户”自动”找到最近节点,核心机制是任播(Anycast)。与单播(Unicast)一个 IP 对应一台主机不同,任播让同一个 IP 地址在多个地理位置同时宣告。
任播的工作原理依赖 BGP与域间路由 的选路机制:
- 每个 PoP 节点通过 BGP 向上游路由器宣告相同的 IP 前缀
- BGP 路由器根据 AS_PATH 长度、策略等属性选择”最优”路径
- 用户的数据包自然被路由到 BGP 意义上”最近”的 PoP
- 无需 DNS 精确调度,网络层自动完成就近接入
任播的”最近”是 BGP 路由意义上的最近,不一定是物理距离最近。BGP 选路受策略、商业关系影响,可能出现物理距离近但 BGP 路径远的情况。因此大型 CDN 通常结合 DNS 调度(GSLB)和任播两种方式——DNS 层面做粗粒度调度,任播做细粒度优化。
2.3 DNS调度 vs 任播
| 维度 | DNS 调度(GSLB) | BGP 任播 |
|---|---|---|
| 调度粒度 | 基于 DNS 解析 IP 粗粒度地域 | 基于 BGP 路由细粒度就近 |
| 响应速度 | DNS 缓存 TTL 延迟(分钟级) | BGP 收敛秒级 |
| 负载均衡 | 可感知节点负载动态调整 | 依赖 BGP 自然分布,可能不均 |
| 故障转移 | DNS TTL 过期后才能切换 | BGP 撤回路由立即生效 |
| 精确度 | 受 DNS 递归服务器位置影响 | 受 BGP 策略影响 |
| 典型用途 | HTTP/HTTPS 流量调度 | DNS 服务、DDoS 防护流量稀释 |
实际部署中,两种方式通常组合使用:DNS 调度将用户引导到合适的区域集群,任播在集群内做就近接入和故障切换。
2.4 PoP节点的内部结构
一个典型的 PoP 节点包含以下组件:
- 边缘路由器:BGP 任播宣告,接收用户流量
- L4 负载均衡:基于 IP+端口做四层分发,通常用 LVS 或等效方案
- L7 代理/缓存:Nginx、Varnish 或自研缓存引擎,处理 HTTP 请求
- SSL 卸载:TLS 终止,将解密后的请求转发给缓存层
- 回源模块:缓存未命中时向源站请求内容
- 日志与监控:访问日志、缓存命中率、回源延迟等指标采集
三、缓存策略
3.1 缓存键与Vary
CDN 缓存的核心问题是:什么决定一个资源是否命中缓存? 答案是缓存键(Cache Key)。
缓存键通常由以下部分组成:
- 请求 URL(含查询参数)
Vary头指定的请求头
Vary 头告诉 CDN:对于同一个 URL,不同的请求头值对应不同的缓存版本。最常见的例子是内容压缩:
# Nginx 缓存配置示例proxy_cache_path /var/cache/cdn levels=1:2 keys_zone=cdn_cache:100m max_size=50g inactive=60m use_temp_path=off;
server { listen 443 ssl; server_name cdn.example.com;
# 缓存键默认是 $scheme$proxy_host$request_uri # Vary: Accept-Encoding 时,gzip 和 br 版本分别缓存 proxy_cache_key $scheme$proxy_host$request_uri$http_accept_encoding;
location /assets/ { proxy_cache cdn_cache;
# 缓存有效期 proxy_cache_valid 200 302 10m; proxy_cache_valid 404 1m;
# 启用陈旧缓存 proxy_cache_use_stale error timeout updating; proxy_cache_background_update on;
# 添加缓存状态头 add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://origin; }}Vary 头使用不当会导致缓存命中率暴跌。Vary: * 等价于禁止缓存;Vary: User-Agent 会因为 UA 字符串的多样性产生大量缓存副本,浪费存储空间。实践中应只对真正影响响应内容的请求头做 Vary。
3.2 Cache-Control详解
HTTP协议演进 中介绍了 Cache-Control 的基本语义,这里从 CDN 角度深入:
| 指令 | 含义 | CDN 行为 |
|---|---|---|
max-age=3600 | 缓存 3600 秒 | 在有效期内直接返回,不回源 |
s-maxage=600 | 共享缓存有效期 600 秒 | CDN 遵循此值而非 max-age |
no-cache | 每次使用前必须验证 | CDN 发送条件请求(304 验证) |
no-store | 不缓存任何内容 | CDN 不存储响应 |
public | 允许共享缓存缓存 | 即使有 Authorization 头也可缓存 |
private | 仅浏览器可缓存 | CDN 不缓存 |
stale-while-revalidate=60 | 允许 60 秒内返回陈旧内容 | 后台异步刷新,用户无感知 |
stale-if-error=300 | 错误时允许返回 300 秒内的陈旧内容 | 源站故障时的容错 |
stale-while-revalidate 是 CDN 场景下特别重要的指令——它让 CDN 在缓存过期时先返回陈旧内容给用户,同时在后台异步回源刷新。用户感知到的延迟始终是缓存命中的延迟,不会因为回源而阻塞。
3.3 缓存决策流程
CDN 收到请求后,缓存决策的完整流程:
3.4 缓存清除与失效
CDN 缓存失效有三种方式:
自然过期:等待 max-age / s-maxage 到期,最简单但不够及时。
主动清除(Purge):通过 API 或管理界面强制清除指定 URL 的缓存:
# Cloudflare 缓存清除 APIcurl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache" \ -H "Authorization: Bearer {api_token}" \ -H "Content-Type: application/json" \ -d '{ "files": [ "https://cdn.example.com/css/style.css", "https://cdn.example.com/js/app.js" ] }'
# 清除所有缓存curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache" \ -H "Authorization: Bearer {api_token}" \ -H "Content-Type: application/json" \ -d '{"purge_everything": true}'缓存键版本化:在 URL 中嵌入版本号或哈希值(如 app.v2a3f5.js),更新时发布新文件名而非覆盖旧文件。这是最可靠的方案——旧版本自然过期,新版本立即生效。
3.5 缓存命中率指标
| 指标 | 含义 | 典型值 |
|---|---|---|
| HIT | 缓存命中,直接返回 | 85-95% |
| MISS | 缓存未命中,回源获取 | 3-8% |
| EXPIRED | 缓存过期,需验证 | 2-5% |
| STALE | 返回陈旧内容,后台刷新 | <1% |
| BYPASS | 请求绕过缓存(如 no-store) | <1% |
缓存命中率是 CDN 最核心的运营指标。90% 以上的命中率意味着 90% 的请求不需要回源,源站带宽和负载大幅降低。
四、回源与动态加速
4.1 回源机制
当 PoP 缓存未命中时,需要向源站请求内容,这个过程叫回源(Origin Pull)。回源的效率直接影响用户感知延迟和源站负载。
Varnish VCL 中回源逻辑的典型配置:
# Varnish VCL 回源配置vcl 4.1;
backend origin { .host = "origin.example.com"; .port = "443"; .ssl = 1; .probe = { .url = "/health"; .interval = 5s; .timeout = 2s; .window = 5; .threshold = 3; }}
sub vcl_recv { # 不缓存的路径直接回源 if (req.url ~ "^/api/") { return (pass); }
# 规范化 Accept-Encoding,减少缓存分片 if (req.http.Accept-Encoding) { if (req.http.Accept-Encoding ~ "br") { set req.http.Accept-Encoding = "br"; } elsif (req.http.Accept-Encoding ~ "gzip") { set req.http.Accept-Encoding = "gzip"; } else { unset req.http.Accept-Encoding; } }}
sub vcl_backend_response { # 遵循源站 Cache-Control,但覆盖 s-maxage if (beresp.http.Cache-Control ~ "s-maxage") { # 使用源站指定的共享缓存时间 } elsif (beresp.status == 200) { # 默认缓存 10 分钟 set beresp.ttl = 10m; set beresp.grace = 1h; # 陈旧容错窗口 }
# 不缓存私有内容 if (beresp.http.Cache-Control ~ "private") { set beresp.uncacheable = true; }}4.2 回源优化
CDN 在回源链路上做了大量优化,减少源站压力和回源延迟:
连接复用:PoP 与源站之间保持长连接池,避免每次回源都经历 TCP连接管理 的三次握手开销。
回源合并(Request Coalescing):多个用户同时请求同一个未缓存资源时,PoP 只发送一次回源请求,其余请求等待结果。这避免了”惊群效应”——热门资源过期瞬间大量回源请求涌向源站。
源站屏蔽(Origin Shield):在 PoP 和源站之间增加一层屏蔽层,所有回源请求先经过屏蔽节点。屏蔽节点缓存命中则直接返回,避免多个 PoP 重复回源。
4.3 动态内容加速
静态资源(图片、CSS、JS)天然适合缓存,但动态内容(API 响应、个性化页面)无法缓存。CDN 对动态内容的加速依赖网络层优化:
| 优化手段 | 原理 | 效果 |
|---|---|---|
| TCP 参数调优 | 增大初始拥塞窗口、启用窗口缩放 | 减少慢启动耗时 |
| 路由优化 | 选择延迟最低的回源路径 | 避免绕行 |
| 连接预建 | 预先建立到源站的 TCP/TLS 连接 | 消除握手延迟 |
| 压缩传输 | 在 PoP 和源站间启用高压缩比 | 减少传输数据量 |
| TLS 优化 | 会话复用、OCSP 装订 | 减少 TLS 开销 |
4.4 静态加速 vs 动态加速
| 维度 | 静态加速 | 动态加速 |
|---|---|---|
| 核心手段 | 边缘缓存 | 网络路径优化 |
| 缓存策略 | 长 TTL、stale-while-revalidate | no-cache 或短 TTL |
| 回源频率 | 低(命中率 >90%) | 高(每次请求都回源) |
| 延迟改善 | 显著(200ms → 20ms) | 中等(200ms → 120ms) |
| 典型内容 | 图片、CSS、JS、视频 | API、个性化页面、实时数据 |
| 带宽节省 | 极大 | 有限 |
| 适用场景 | 内容分发 | 连接加速 |
五、CDN安全
5.1 DDoS防护:在边缘稀释流量
CDN 的分布式架构天然适合 DDoS 防护——攻击流量被分散到全球数百个 PoP 节点,每个节点只需承受总攻击流量的一小部分。这就是”流量稀释”效应。
如果没有 CDN,100Gbps 的攻击流量直接打向源站,源站的 10Gbps 出口瞬间瘫痪。有了 CDN,攻击流量被分散到数百个 PoP,每个 PoP 只承受几百 Mbps,远在其处理能力之内。清洗后的正常流量再回源,源站几乎无感知。
5.2 WAF:应用层防护
CDN 边缘部署的 WAF(Web Application Firewall)在应用层拦截恶意请求:
- SQL 注入检测:匹配请求参数中的 SQL 关键字和模式
- XSS 防护:检测和过滤跨站脚本攻击载荷
- CC 攻击防护:识别高频访问的 IP,触发速率限制或验证码
- 虚拟补丁:在官方补丁发布前,通过 WAF 规则临时阻断已知漏洞利用
# Nginx WAF 规则示例(ModSecurity 风格)SecRuleEngine On
# 阻止 SQL 注入SecRule ARGS "(?i)(union.*select|insert.*into|delete.*from|drop.*table)" \ "id:1001,phase:2,deny,status:403,msg:'SQL Injection detected'"
# 阻止 XSSSecRule ARGS "(?i)(<script|javascript:|onerror\s*=|onload\s*=)" \ "id:1002,phase:2,deny,status:403,msg:'XSS attack detected'"
# 速率限制:单 IP 每秒不超过 50 请求limit_req_zone $binary_remote_addr zone=api_limit:10m rate=50r/s;limit_req zone=api_limit burst=100 nodelay;5.3 Bot管理
CDN 的 Bot 管理区分善意爬虫和恶意机器人:
- 搜索引擎爬虫:验证 Googlebot/Bingbot 的反向 DNS,放行真爬虫,拦截伪造 UA
- 自动化攻击:检测请求模式(频率、间隔、行为序列),识别自动化工具
- 凭据填充:检测大量登录尝试,触发 CAPTCHA 或封禁
- 爬虫防护:对敏感内容启用 JavaScript 挑战,阻止简单爬虫
5.4 TLS卸载
CDN 在边缘节点终止 TLS 连接,将解密后的请求转发给内部缓存或回源。这带来两个好处:
性能:TLS 握手在边缘完成,用户到 PoP 的 RTT 更短,握手更快。PoP 到源站的内网链路可以复用长连接,避免重复握手。
证书管理:源站不需要暴露私钥,CDN 统一管理证书的申请、续期和部署。Let’s Encrypt 自动化续期在 CDN 规模下尤其有价值。
5.5 源站屏蔽
源站屏蔽(Origin Shield / Shield PoP)是安全与性能的双重保障:
- 所有回源请求经过指定的屏蔽节点,屏蔽节点缓存命中则直接返回
- 屏蔽节点是回源的唯一出口,源站只需放行屏蔽节点的 IP
- 攻击者无法直接访问源站 IP,即使 CDN 被绕过,源站仍有保护
六、动手实践:观察CDN行为
6.1 用curl识别CDN
# 查看响应头,识别 CDN 特征curl -I https://www.cloudflare.com
# 典型输出:# HTTP/2 200# server: cloudflare# cf-ray: 8a1b2c3d4e5f6g7h-IAD# cf-cache-status: HIT# age: 42# vary: Accept-Encoding
# 关键字段解读:# server: cloudflare → CDN 提供商# cf-ray: 请求追踪 ID + PoP 代码(IAD = 华盛顿杜勒斯)# cf-cache-status: HIT/MISS/EXPIRED 缓存状态# age: 缓存已存储的秒数不同 CDN 提供商的响应头特征:
| CDN 提供商 | 特征头 | PoP 代码位置 |
|---|---|---|
| Cloudflare | cf-ray、cf-cache-status | cf-ray 后缀(如 -IAD) |
| Akamai | x-akamai-transformed、x-cache | x-cache 值含 PoP 信息 |
| AWS CloudFront | x-amz-cf-id、x-cache | x-amz-cf-pop 头 |
| Fastly | x-served-by、x-cache | x-served-by 含 PoP 代码 |
| 阿里云 CDN | x-swift-cachetime、ali-swift-global-savetime | x-swift-sf 含节点信息 |
6.2 用dig观察CDN的DNS调度
# 查询 CDN 域名的 DNS 解析链路dig +trace cdn.example.com
# 第一步:权威 DNS 返回 CNAME# cdn.example.com. 300 IN CNAME cdn.example.com.cdn.cloudflare.net.
# 第二步:CDN 的 GSLB 返回 PoP IP# cdn.example.com.cdn.cloudflare.net. 30 IN A 104.16.132.229
# 从不同地理位置查询,对比返回的 IP# 北京 DNS 服务器查询dig @223.5.5.5 cdn.example.com# 返回亚太 PoP 的 IP
# 纽约 DNS 服务器查询dig @8.8.8.8 cdn.example.com# 返回美东 PoP 的 IP
# 查看 CNAME 链dig cdn.example.com CNAME +short# cdn.example.com.cdn.cloudflare.net.6.3 用traceroute追踪到PoP
# 追踪到 CDN 节点的路径traceroute cdn.example.com
# 观察最后几跳的 IP 和 RTT# 如果 RTT 在 10-30ms,说明命中了本地 PoP# 如果 RTT 在 100ms+,可能被路由到了远端 PoP
# 使用 mtr 持续监测mtr --report cdn.example.com
# 对比直接到源站的路径traceroute origin.example.com# 通常 RTT 远大于到 CDN PoP 的 RTT6.4 Chrome DevTools观察CDN头
# 用 curl 模拟浏览器请求,观察完整头部curl -s -D - https://cdn.example.com/image.jpg -o /dev/null
# 输出关键头部:# HTTP/2 200# content-type: image/jpeg# cache-control: public, max-age=86400, s-maxage=3600# age: 1847# x-cache: HIT# x-cache-hits: 42# via: 1.1 varnish (Varnish/7.3)# cf-cache-status: HIT# cf-ray: 8a1b2c3d4e5f-LAX
# 解读:# s-maxage=3600 → CDN 缓存 1 小时# age=1847 → 已缓存 1847 秒(约 30 分钟)# x-cache: HIT → 缓存命中# cf-ray 后缀 LAX → 洛杉矶 PoP在 Chrome DevTools 中观察更直观:
- 打开 DevTools → Network 面板
- 右键列头 → 勾选
Age、Cache-Control、X-Cache - 刷新页面,观察每个资源的缓存状态
- 对比首次加载(MISS)和再次刷新(HIT)的差异
七、本章小结
| 主题 | 核心要点 |
|---|---|
| CDN 动机 | 缩短物理距离、消除单点故障、降低带宽成本 |
| 任播路由 | 同一 IP 多点宣告,BGP 自动就近路由,配合 DNS 调度 |
| 缓存策略 | Cache Key + Vary 决定缓存粒度,s-maxage 控制共享缓存,stale-while-revalidate 异步刷新 |
| 回源优化 | 连接复用、请求合并、源站屏蔽减少回源压力 |
| 动态加速 | TCP 调优、路由优化、连接预建弥补缓存无法覆盖的场景 |
| CDN 安全 | 边缘流量稀释抗 DDoS、WAF 应用层防护、TLS 卸载、源站屏蔽 |
CDN 把数据搬到离用户更近的地方,本质上是互联网”端到端”原则的一次务实妥协——在纯粹的端到端和极致的用户体验之间,CDN 选择了后者。理解了 CDN 的缓存、调度和安全机制,再看 DNS域名系统 的 CNAME 委派、BGP与域间路由 的任播宣告、HTTP协议演进 的缓存语义,会发现它们都在为 CDN 这张分布式网络服务。
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






