CDN 与 WAF 精讲教程 / 第02章 CDN 基础原理
第02章 CDN 基础原理
本章深入 CDN 的核心工作原理,包括边缘节点、源站、缓存机制、回源策略和 DNS 调度算法。理解这些是掌握后续章节的基础。
2.1 边缘节点(Edge Node)
2.1.1 什么是边缘节点
边缘节点是 CDN 部署在各地的缓存服务器集群,是用户请求的第一落点。每个 PoP(Point of Presence)由大量服务器组成,配备大容量 SSD/HDD 存储和高速网络。
2.1.2 节点层次结构
┌──────────────────────────────────────────────────────────┐
│ CDN 节点层次 │
│ │
│ ┌─────────────┐ 高流量区域,直接服务用户 │
│ │ 边缘节点 │ ┌──────────────────────────┐ │
│ │ (Edge) │───→│ 缓存内容 → 直接响应 │ │
│ └──────┬──────┘ └──────────────────────────┘ │
│ │ Cache Miss │
│ ▼ │
│ ┌─────────────┐ 区域级缓存,减轻源站压力 │
│ │ 中间节点 │ ┌──────────────────────────┐ │
│ │ (Mid-Tier) │───→│ 缓存回源内容 → 缓存共享 │ │
│ └──────┬──────┘ └──────────────────────────┘ │
│ │ Cache Miss │
│ ▼ │
│ ┌─────────────┐ ┌──────────────────────────┐ │
│ │ 源站 │───→│ 原始内容服务器 │ │
│ │ (Origin) │ └──────────────────────────┘ │
│ └─────────────┘ │
└──────────────────────────────────────────────────────────┘
2.1.3 全球节点分布参考
| 厂商 | 全球 PoP 数量 | 覆盖区域 | 特点 |
|---|---|---|---|
| Cloudflare | 300+ | 全球 120+ 国家 | Anycast,免费接入 |
| Akamai | 4,000+ | 全球 130+ 国家 | 最广覆盖 |
| AWS CloudFront | 600+ | 全球 90+ 国家 | 集成 AWS 生态 |
| 阿里云 CDN | 3,200+ | 全球 70+ 国家 | 国内节点最多 |
| 腾讯云 CDN | 2,800+ | 全球 50+ 国家 | 与腾讯业务协同 |
2.1.4 节点内部架构
单个边缘节点通常包含:
| 组件 | 作用 | 典型配置 |
|---|---|---|
| L4 负载均衡器 | TCP/UDP 流量分发 | DPVS / LVS |
| L7 代理服务器 | HTTP 请求处理与缓存 | Nginx / Varnish / Envoy |
| 缓存存储层 | 存储缓存内容 | SSD + HDD 分层存储 |
| 监控 Agent | 采集节点指标 | Prometheus + 自研 |
2.2 源站(Origin)
2.2.1 源站类型
| 类型 | 协议 | 场景 | 示例 |
|---|---|---|---|
| Web Server | HTTP/HTTPS | 网站静态文件 | Nginx / Apache / Caddy |
| Object Storage | S3/OSS | 大规模静态资源 | AWS S3 / 阿里云 OSS |
| Load Balancer | HTTP/HTTPS | 动态请求分发 | ALB / Nginx Upstream |
| Media Server | HLS/DASH | 视频点播/直播 | 自建 / AWS MediaConvert |
2.2.2 源站配置建议
源站高可用架构:
┌──────────────┐
│ CDN 节点 │
└──────┬───────┘
│ 回源请求
▼
┌──────────────────┐
│ 源站负载均衡 │ ← SLB / NLB
└────────┬─────────┘
│
┌─────────┼─────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ App-1 │ │ App-2 │ │ App-3 │ ← 多实例
└────────┘ └────────┘ └────────┘
│ │ │
└─────────┼─────────┘
▼
┌──────────┐
│ 数据库 │ ← 主从/集群
└──────────┘
关键原则:源站应至少部署 2 个可用区(AZ),避免单点故障。CDN 回源失败会直接返回错误给用户。
2.3 缓存机制
2.3.1 HTTP 缓存头
CDN 依赖 HTTP 响应头决定缓存行为:
| 头部 | 作用 | 示例 |
|---|---|---|
Cache-Control | 最主要的缓存控制头 | public, max-age=86400 |
Expires | 过期时间(已被 Cache-Control 取代) | Thu, 31 Dec 2026 23:59:59 GMT |
ETag | 资源版本标识 | "5f7b3d8a-1234" |
Last-Modified | 最后修改时间 | Wed, 10 May 2026 12:00:00 GMT |
Vary | 告知缓存键应包含哪些请求头 | Vary: Accept-Encoding |
CDN-Cache-Control | CDN 专用缓存指令 | max-age=604800 |
Surrogate-Control | 代理专用缓存指令 | max-age=604800 |
2.3.2 Cache-Control 常用指令
Cache-Control 指令速查:
┌──────────────────────────────────────────────────────────┐
│ public → 任何缓存都可以存储(CDN 可缓存) │
│ private → 仅浏览器可缓存(CDN 不缓存) │
│ no-cache → 每次使用前必须验证(不是不缓存!) │
│ no-store → 完全不缓存 │
│ max-age=N → 缓存 N 秒后过期 │
│ s-maxage=N → 共享缓存(CDN)过期时间,覆盖 max-age │
│ stale-while-revalidate=N → 过期后 N 秒内可使用旧缓存 │
│ must-revalidate → 过期后必须向源站验证 │
│ immutable → 资源永不变,不需验证 │
└──────────────────────────────────────────────────────────┘
2.3.3 缓存状态码
| 状态 | 含义 | 说明 |
|---|---|---|
HIT | 缓存命中 | 直接从边缘节点响应 |
MISS | 缓存未命中 | 已向源站回源 |
EXPIRED | 缓存过期 | 已过期,正在回源验证 |
STALE | 陈旧缓存 | 使用过期缓存(源站不可达时) |
REVALIDATED | 重新验证 | ETag/Last-Modified 验证后使用缓存 |
BYPASS | 绕过缓存 | 请求命中绕过规则 |
DYNAMIC | 动态内容 | 不可缓存的动态响应 |
2.4 回源策略(Origin Pull)
2.4.1 何时触发回源
用户请求 → 边缘节点
│
├── 情况1: 缓存命中(HIT)→ 直接返回缓存
│
├── 情况2: 缓存未命中(MISS)→ 回源
│ ├── 首次请求
│ ├── 缓存已过期(TTL Expired)
│ ├── 缓存被主动清除(Purge)
│ └── Vary 头变化导致新缓存键
│
└── 情况3: 请求不应缓存(BYPASS/DYNAMIC)
├── Cache-Control: no-store / private
├── 请求方法非 GET/HEAD
└── 命中不缓存规则
2.4.2 回源验证机制
当缓存过期时,CDN 会使用条件请求向源站验证:
| 验证方式 | 请求头 | 场景 | 网络开销 |
|---|---|---|---|
| ETag 验证 | If-None-Match: "etag-value" | 精确版本匹配 | 低(304 响应体为空) |
| 时间验证 | If-Modified-Since: <date> | 基于修改时间 | 低(304 响应体为空) |
| 直接回源 | 无条件头 | 缓存未存储 | 高(传输完整内容) |
2.4.3 回源优化
| 策略 | 说明 | 效果 |
|---|---|---|
| 回源合并 | 同一资源多个并发请求合并为一次回源 | 减少源站 QPS 90%+ |
| Stale-While-Revalidate | 过期后在后台异步回源 | 用户无感知延迟 |
| 预热(Preheat) | 提前将热门资源推送到边缘 | 大促/发布前必备 |
| 回源盾(Shield) | 指定一个节点作为回源中转 | 减少源站出口流量 |
2.5 DNS 调度
2.5.1 DNS 解析流程
DNS 调度是 CDN 就近分配的核心机制:
用户 (北京) 请求 cdn.example.com
│
├── 1. 浏览器缓存 → 未命中
├── 2. OS 缓存 → 未命中
├── 3. 本地 DNS (LDNS) → 未命中
│
▼
4. 递归查询
│
├── Root DNS → 返回 .com NS
├── .com DNS → 返回 example.com NS
├── example.com DNS → CNAME → cdn.example.cdnprovider.net
│
▼
5. CDN 厂商 DNS (调度系统)
│
├── 识别 LDNS 来源 IP(或 EDNS Client Subnet)
├── 查询北京区域最优节点
│
▼
6. 返回 103.x.x.x(北京 PoP IP)
│
▼
用户浏览器 → 连接到 103.x.x.x(北京边缘节点)
2.5.2 DNS 调度算法
| 算法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| GeoDNS | 根据 LDNS IP 地理位置分配 | 简单高效 | LDNS 位置 ≠ 用户位置 |
| EDNS Client Subnet (ECS) | 携带用户真实 IP 子网 | 精准定位 | 需 LDNS 支持 |
| Anycast | 多节点共享同一 IP,BGP 路由选路 | 天然就近 | 路由收敛慢 |
| 延迟探测 | 边缘节点主动探测 LDNS 延迟 | 最优延迟 | 系统复杂度高 |
| 负载均衡 | 考虑节点负载和健康状态 | 避免过载 | 可能牺牲距离 |
2.5.3 TTL 策略
| 场景 | TTL 建议 | 原因 |
|---|---|---|
| 稳定业务 | 300-600s | 减少 DNS 查询,稳定调度 |
| 高可用要求 | 60-120s | 快速切换故障节点 |
| 即将迁移 | 30-60s | 平滑切换 |
| DDoS 防护 | 极短或依赖 Anycast | 快速切换到清洗中心 |
2.6 典型业务场景
2.6.1 静态网站加速
场景:个人博客 / 企业官网
缓存策略:
├── HTML: max-age=3600, s-maxage=86400 (CDN 缓存 1 天)
├── CSS/JS: max-age=31536000, immutable (1 年,文件名带 hash)
├── 图片: max-age=2592000 (30 天)
└── 字体: max-age=31536000, immutable (1 年)
2.6.2 API 加速
场景:移动端 API 接口
策略:
├── 使用 CDN 动态加速(TCP 优化、路由优化)
├── 不缓存 POST/PUT/DELETE 请求
├── GET 接口可选择性缓存(如商品详情 5-60 秒)
└── 开启 WebSocket 支持(实时通信场景)
2.7 注意事项
⚠️ 缓存一致性:CDN 缓存与源站之间存在时间窗口差异,更新内容时需要主动 Purge。
⚠️ LDNS 劫持:部分 ISP 会劫持 DNS 解析,导致用户被调度到非最优节点。
⚠️ 回源风暴:大批量缓存同时过期可能导致瞬间大量回源(Thundering Herd),应使用
stale-while-revalidate或错开 TTL。⚠️ 缓存键设计:URL 中的无意义参数(如
?utm_source=xxx)应被忽略,避免同一内容生成多个缓存副本。
2.8 扩展阅读
- RFC 9111 - HTTP Caching — HTTP 缓存最新规范
- RFC 7871 - Client Subnet in DNS Queries (EDNS) — EDNS Client Subnet 规范
- Cloudflare: How CDN caching works — CDN 缓存原理入门
- Varnish Book — Varnish 缓存引擎权威指南
本章小结
| 主题 | 核心要点 |
|---|---|
| 边缘节点 | 用户第一落点,Cache HIT 则直接响应 |
| 源站 | 内容唯一来源,需高可用部署 |
| 缓存机制 | HTTP Cache-Control 驱动,支持条件验证 |
| 回源策略 | 合并回源、预热、回源盾减少源站压力 |
| DNS 调度 | GeoDNS + ECS 实现就近分配 |
下一章:第03章 CDN 架构设计 →