你要为一个全球性的电商 / 媒体站设计 CDN + Edge 层:5000 万 DAU、分布在五大洲,内容是静态资产(图片/JS/视频分片,可强缓存)+ 半动态页面(商品页,秒级可 stale)+ 动态 API(购物车,不可缓存)混合。
核心矛盾:把内容推到离用户越近、命中率越高、延迟越低,但失效越难、一致性越弱。CDN 的设计就是在这条轴上找平衡。
graph LR
U["全球用户
同一个 Anycast IP"]
subgraph PoP["最近 PoP (lower tier)"]
EDGE["边缘节点
TLS 终止 · WAF · Edge Worker"]
L1["边缘缓存 L1"]
end
UPPER["区域上层缓存
upper tier"]
SHIELD["Origin Shield
单点回源收敛"]
ORG[("Origin
app + 对象存储")]
U -->|BGP 选最近| EDGE --> L1
L1 -.miss.-> UPPER
UPPER -.miss.-> SHIELD
SHIELD -.miss.-> ORG
classDef client fill:#1a2530,stroke:#64c8ff,color:#e8eef5
classDef edge fill:#0e2030,stroke:#5eead4,color:#e8eef5
classDef cache fill:#1a1a30,stroke:#ffb450,color:#e8eef5
classDef origin fill:#2a1530,stroke:#ff7ab6,color:#e8eef5
class U client
class EDGE,L1 edge
class UPPER,SHIELD cache
class ORG origin
Anycast 把用户引到最近 PoP;缓存分层收敛回源,越往右节点越少、命中越贵
组件职责:边缘节点用 Anycast 接住流量,做 TLS 握手、WAF 过滤、运行 Edge Worker;边缘缓存命中则直接返回(占绝大多数请求);未命中向区域上层缓存请求,再不行才到 Origin Shield(一个指定 PoP 专门回源,把成千上万边缘的 miss 收敛成对 origin 的少量请求),最后才打 origin。这条「下层 → 上层 → shield → origin」的漏斗是命中率与 origin 保护的关键。
核心 trade-off:用路由层做负载均衡,换来零配置故障转移,但牺牲了对「用户落哪个节点」的精确控制。
原理:同一段 IP 前缀从全球几百个 PoP 用 BGP 同时宣告。用户的数据包由互联网路由器按 BGP 度量(AS 跳数等)送到「网络上最近」的 PoP——通常也是延迟最低的。这带来三个白送的好处:就近接入(无需 GeoDNS 猜测)、DDoS 稀释(攻击流量被分摊到所有 PoP,单点容量 = 全网容量)、自动 failover(PoP 宕机时撤回路由宣告,BGP 在秒级把流量收敛到次近 PoP,无需改 DNS、不等 TTL 过期)。
核心 trade-off:边缘节点越多命中率越分散,分层把 miss 收敛回源、保护 origin,代价是多一跳延迟。
原理:CDN 用 cache key(默认 host + path + 部分 query)索引对象。但全球几百个 PoP 各自独立缓存时,每个 PoP 第一次都要回源——命中率被「摊薄」,origin 仍会被几百倍放大的 miss 打到。Tiered Cache 把 PoP 分成下层(近用户)和上层(少数大节点):下层 miss 先问上层,只有上层 miss 才允许回源。这样 origin 看到的是少数上层节点的请求,而非全部边缘。配合 request coalescing(同一 key 的并发 miss 在节点内合并成一次回源,其余请求挂起等结果),可彻底压住 thundering herd。
# 伪代码:边缘节点处理请求(含 coalescing)
def handle(req):
key = cache_key(req.host, req.path, normalize(req.query))
obj = cache.get(key)
if obj and not obj.expired():
return obj # 命中,绝大多数走这里
# miss:同 key 只放一个回源,其余等待(single-flight)
with coalesce(key):
obj = cache.get(key) # 双检:可能别人刚填好
if obj and not obj.expired():
return obj
obj = fetch_from_upper_or_origin(key) # tiered:先问上层
cache.set(key, obj, ttl=obj.cache_control_ttl())
return obj
核心 trade-off:主动 purge 一致性强但传播有延迟、有放大;TTL 简单但要么太旧要么打爆 origin。
原理:边缘缓存有几百个副本,「让全球同时失效」本质是一个分布式广播问题。三种手段:① TTL 过期(被动,最简单,但 TTL 短=回源多、TTL 长=数据旧);② 显式 purge(按 URL / 按 tag / purge everything,主动推送失效消息到所有 PoP,秒级但有传播延迟,purge everything 会瞬间清空缓存→origin 被打爆);③ stale-while-revalidate / soft purge(标记为陈旧但仍先返回旧值,后台异步回源刷新)——用「短暂 stale」换「永不让用户等回源、永不雪崩 origin」。生产里通常是 长 TTL + tag-based purge + SWR 的组合:正常靠 tag 精确失效,兜底靠 TTL,体验靠 SWR。
stale-while-revalidate 作为标准缓存指令降低回源毛刺。核心 trade-off:isolate 启动近零、密度高但每请求受限;container/VM 能力强但冷启动慢、密度低。
原理:把鉴权、A/B 分流、个性化、API 聚合等逻辑从 origin 下沉到边缘,省一整趟回源 RTT。但「在几百个 PoP 跑用户代码」要求极高的密度和极低的冷启动。两条路线:V8 isolate(Cloudflare Workers)——单进程内跑成千上万个轻量沙箱,冷启动近乎为零、内存开销极小,但单请求 CPU/内存有硬上限、不能跑任意原生二进制;容器 / 微 VM(Lambda@Edge 等)——能力接近完整运行时,但冷启动以百毫秒计、单节点密度低。Edge 的另一难点是状态:边缘天然无共享状态,强一致数据要么回源、要么用专门的边缘存储(KV 最终一致、Durable Object 单点串行化)。
| 方案 | 冷启动 | 密度/成本 | 限制 |
|---|---|---|---|
| V8 Isolate(Workers) | ~0(无进程启动) | 极高 | JS/WASM、CPU/内存硬上限 |
| 容器/微 VM(Lambda@Edge) | 百毫秒级 | 低 | 能力全但贵、PoP 覆盖少 |
| 纯 CDN 规则(无代码) | — | 最高 | 只能做声明式重写/路由 |
Vary 控制。origin 承接的是 miss 流量 = 总 RPS × (1 − 命中率)。200 万 RPS 下:命中率 90% → miss 20 万;命中率 80% → miss 40 万,翻倍。命中率每掉 10 个百分点,origin 压力的相对增幅远大于 10%——这是非线性的:从 99% 掉到 98%,miss 直接翻倍(1% → 2%)。
所以命中率是 origin 雪崩的先行指标:等到 origin CPU 报警时,缓存层往往已经失守一阵。常见诱因:cache key 误带参数、大规模 purge、新版本改了 Cache-Control、热点内容 TTL 集体过期(要加 TTL jitter)。监控应对命中率本身设阈值告警,而非只看 origin。
关键认识:不要把易变的价格塞进可被边缘强缓存的 HTML。分离策略:
/p/123?v=789),调价即换 URL——旧 URL 的缓存自然作废,无需等 purge 传播。本质:用「缓存可缓存的、实时取易变的」替代「缓存一切再想办法快速失效」。purge 永远有窗口,架构上消除依赖才是稳的。
单数据中心的抗攻击容量 = 那一个 数据中心的带宽/清洗能力。攻击者只要打出超过它的流量就能压垮——你被迫按「单点峰值」堆容量,极其昂贵。
Anycast 下,同一 IP 在全球几百个 PoP 宣告,攻击流量按源头地理被 BGP 自动分散到各 PoP:来自欧洲的攻击落欧洲 PoP,来自亚洲的落亚洲 PoP。于是有效抗攻击容量 ≈ 全网总容量,而非单点。攻击者要打垮你,得同时压垮全球所有 PoP。再叠加每 PoP 的清洗能力,单点故障也只丢一个区域(BGP 撤回路由后流量转移)。这就是为什么主流 CDN 把「Anycast + 大量 PoP」当作 DDoS 防护的地基。
代价:你失去对流量落点的精确控制,且需要全球 BGP 与海量 PoP 的运营能力——这正是 CDN 作为「专门生意」存在的原因。
isolate 在单进程内跑成千上万个 V8 沙箱,省掉了容器/VM 的进程与内核隔离开销——所以冷启动近零、单机密度极高、成本低。代价:
所以选型是「海量轻量短请求 选 isolate;重逻辑/需完整运行时 选容器/微 VM」。Cloudflare 赌的是边缘场景绝大多数属于前者。
Cloudflare 2019、Fastly 2021 都是「一个改动瞬时全球生效」造成的全网级事故。把边缘当成最高风险的发布面来治理:
一句话:边缘的便利来自「全局即时生效」,而安全来自给这个全局生效加上灰度、限额和开关。