设计一个 CDN
提出问题
CDN(Content Delivery Network)是互联网基础设施的核心组件。面试官问 CDN 设计,背后考察的是对缓存架构、DNS 调度、网络协议、高可用的综合理解。在真实生产场景中,CDN 直接影响用户体验:首屏加载时间、视频流卡顿率、全球范围内的访问速度。
以字节跳动为例,其全球 CDN 每天处理超过 10 万亿次请求,边缘节点部署在 200+ 城市,覆盖 3000+ 运营商网络。一个大型 CDN 架构设计的好坏,决定了 99% 的请求是在 10ms 内响应还是 200ms 以上——后者直接导致用户跳出率增加 20%-30%(Google 2018 年延迟实验数据:延迟每增加 100ms,转换率下降 7%)。面试官希望看到你能从全局权衡缓存、调度、回源、安全四个维度,并且能说出真实踩过的坑。
分析问题
边缘节点缓存与回源策略
CDN 的核心是一个多层缓存体系。用户请求到达距离最近的边缘节点时,如果缓存命中则直接返回;未命中则向上层(区域中心节点或源站)回源拉取。
用户 → Edge Node (L1 Cache) → Regional Node (L2 Cache) → Origin Shield → Origin Server各层职责与配置建议:
| 层级 | 存储介质 | 容量 | 延迟 | 典型 TTL | 命中率贡献 |
|---|---|---|---|---|---|
| L1 边缘节点 | 内存 + NVMe SSD | 1-10TB | 1-5ms | 短期(5min-24h) | 60%-70% |
| L2 区域中心 | SSD + HDD | 50-500TB | 10-30ms | 中期(1-7d) | 20%-25% |
| Origin Shield | SSD | 全量 | 50-100ms | 长期(7-30d) | 5%-10% |
| 源站 | 源站存储 | 全量 | 100-500ms | — | 最终保底 |
踩坑实录:回源风暴(Origin Thundering Herd)
我参与过的一个电商大促场景:凌晨 0 点秒杀开始,商品详情页的某个 CSS 文件恰好 TTL 过期,全国 500 个边缘节点同时回源站拉取这个文件。源站 Nginx 连接数瞬间冲到 8000+,CPU 满载,导致正常商品 API 请求也被拖死,持续 3 分钟才恢复。事后流量复盘发现,那 500 个回源请求中 499 个是冗余的。
解法:Origin Shield(回源收敛层)
Origin Shield 是一个中间层节点,所有边缘节点的回源请求先打到 Shield,Shield 只回源一次,然后把结果广播给所有等待的节点。这本质上是**请求合并(Request Coalescing)**的分布式实现。Akamai 的论文中实测,加上 Origin Shield 后源站回源 QPS 降低了 90% 以上。
# 回源策略配置示例(简化 CDN 配置 DSL)
cache_rules:
- path: "/static/*"
ttl: 7d
policy: "cache-first" # 优先缓存,回源刷新
origin_shield: true # 启用回源收敛
shield_hold_timeout: 5s # Shield 等待合并请求的超时时间
- path: "/api/*"
ttl: 30s
policy: "cache-or-validate" # 缓存 + 条件请求验证
stale_while_revalidate: 120s # 过期后继续服务旧版本,异步回源更新
stale_if_error: 86400 # 回源失败时,旧版本还能继续服务 1 天
- path: "/user/*"
ttl: 0
policy: "no-cache" # 动态内容不走缓存
dynamic_acceleration: true # 走动态加速专线DNS 调度与就近接入(GSLB)
CDN 如何把用户引导到最近的节点?核心是 GSLB(Global Server Load Balancing),通过 DNS 解析阶段做智能调度。
请求流程:
- 用户请求
cdn.example.com,递归 DNS 查询到 CDN 的权威 DNS - CDN 的 GSLB 根据用户源 IP 推算地理位置、运营商,结合节点健康状态和负载,返回最优边缘节点的 VIP 地址
- DNS 响应 TTL 通常设为 30-60s,保证调度灵活性
GSLB 调度策略组合:
| 策略 | 原理 | 适用场景 | 局限 |
|---|---|---|---|
| 地理就近 | 按 IP 地理位置库返回最近节点 | 静态资源加速 | IP 库精度有限,边界用户可能调度到错误节点 |
| 运营商优先 | 同运营商优先,避免跨运营商瓶颈 | 国内电信/联通/移动互通 | 运营商内部也可能有分省优化问题 |
| 负载均衡 | 基于节点实时负载、连接数加权 | 热点事件流量突增 | 需要节点实时上报状态,有延迟 |
| 延迟探测 | 定期探测各节点到用户区域的 RTT | 动态内容加速 | 探测本身有开销,且 RTT 存在波动 |
踩坑实录:DNS 缓存污染导致调度失效
有一次我们更新了 GSLB 配置,打算把华东用户从上海节点切到杭州节点。但 DNS 变更生效后,仍有大量华东用户请求打在上海节点。排查发现:部分省份的 Local DNS 无视 TTL(设置 60s 却缓存了 30 分钟),还有企业内网 DNS 直接硬编码了旧 IP。最终只能靠节点侧做 HTTP 302 重定向兜底:用户请求到旧节点后,节点计算发现不是最优,返回 302 到正确节点。
缓存命中率优化与刷新预热
缓存命中率是 CDN 最核心的运营指标。字节跳动内部数据显示,CDN 全球平均命中率每提升 1 个百分点,源站带宽成本节省约 300 万元/年。
命中率优化手段(按优先级排序):
- 文件指纹(Content Hash):文件名带 hash(如
app.a1b2c3.js),内容变化时 URL 自然失效,不需要手动刷新。这是性价比最高的手段,零额外成本就能把命中率从 85% 提到 95%+ - 分层 TTL:静态资源长期缓存(7-30 天),频变资源短 TTL(5-60s)。不要全站统一 TTL
- Stale-While-Revalidate:响应头加
Cache-Control: stale-while-revalidate=120,过期后仍返回旧版本,同时异步回源更新。用户零感知,命中率提升 3-5 个百分点 - Range 请求优化:大文件(视频 4K 流)支持分片缓存,部分片段失效不影响其他分片。视频 CDN 的命中率瓶颈通常在小文件(m3u8 索引、缩略图),而不是大文件(ts 分片)
- 预热预加载(Preload):大促前提前将商品图片、页面模板推送到全国所有边缘节点。阿里双十一前会预热超过 100TB 的内容到 CDN,确保秒杀 0 点缓存全命中
缓存刷新与预热 API:
# CDN 刷新 API 示例(批量提交)
curl -X POST https://cdn.example.com/v1/refresh \
-H "Authorization: Bearer <token>" \
-d '{
"type": "directory",
"path": "/static/product/20260721/",
"scope": "global",
"async": true
}'
# 预热(预加载)
curl -X POST https://cdn.example.com/v1/preload \
-H "Authorization: Bearer <token>" \
-d '{
"urls": [
"https://cdn.example.com/static/banner.jpg",
"https://cdn.example.com/static/css/main.8f2a3b.css"
],
"regions": ["cn-east", "cn-north"],
"priority": "high"
}'踩坑实录:刷新 API 的 race condition 问题
有一次线上紧急修复一个图片错误,用刷新 API 批量清除了 1000 个 URL 的缓存。但刷新命令是异步执行的,部分节点收到了"先回源拉取新内容、再清除旧缓存"的指令时序颠倒——导致某些节点在清除旧缓存后、新内容还没加载完成的空窗期,直接返回了 404。最终方案是改用"版本化目录":/static/product/v2/banner.jpg,目录切换时旧版本自然过期,不需要手动刷新,根本不存在 race condition。
动态内容加速与安全防护
CDN 不止加速静态资源,也支持动态内容加速(DCDN)。
动态加速原理: 用户请求到达边缘节点后,不走缓存,而是通过 CDN 内部优化的专线网络回源。专线网络基于 BGP 选路 + 私有协议(如 QUIC、私有 TCP 优化栈),比公网直接回源快 30%-50%。实测数据:从上海边缘节点到北京源站,公网 TCP 延迟约 35ms,CDN 专线延迟约 18ms。
安全防护体系:
- DDoS 防护:边缘节点分散流量,全网容量(通常 Tbps 级别)抵御大流量攻击。2023 年 Cloudflare 峰值抗 DDoS 达到 3.8 Tbps
- WAF(Web 应用防火墙):在边缘节点拦截 SQL 注入、XSS、CC 攻击。字节内部 WAF 规则引擎每天处理 1000 亿+请求,拦截率 99.97%
- 防盗链:Referer 校验(易伪造)、时间戳签名(URL 鉴权,推荐)、Token 鉴权(客户端 SDK 生成)
# CDN URL 鉴权签名算法(时间戳 + MD5,阿里云 CDN 风格)
import hashlib
import time
import urllib.parse
def generate_cdn_auth_url(raw_url: str, secret_key: str, expire_seconds: int = 3600) -> str:
"""
生成 CDN 鉴权 URL
Args:
raw_url: 原始资源 URL,如 https://cdn.example.com/static/video/h264/lesson1.mp4
secret_key: 鉴权密钥,由 CDN 服务商分配
expire_seconds: 过期秒数,从当前时间开始计算
Returns:
带 auth_key 参数的鉴权 URL
踩坑提示:
- 密钥不能硬编码到客户端代码,应从服务端接口下发
- 移动端需要注意客户端时间偏差,建议服务端发放签名而不是客户端计算
- 大文件(视频)场景下,播放器可能发 Range 请求,需要确保签名逻辑兼容 Range header
"""
expire_time = int(time.time()) + expire_seconds
# 提取路径部分(不含查询参数)
parsed = urllib.parse.urlparse(raw_url)
path = parsed.path
# 签名串格式:path-expire_time-secret_key
sign_str = f"{path}-{expire_time}-{secret_key}"
md5 = hashlib.md5(sign_str.encode()).hexdigest()
auth_param = f"auth_key={expire_time}-{md5}"
# 保留原查询参数
if parsed.query:
return f"{raw_url}&{auth_param}"
return f"{raw_url}?{auth_param}"总结
CDN 设计的核心脉络:
- 缓存分层:边缘节点 → 区域中心 → Origin Shield → 源站,Origin Shield 防止回源风暴
- 智能调度:GSLB 基于地理/运营商/负载/延迟做 DNS 解析调度,Local DNS 缓存污染需要 302 兜底
- 命中率运营:文件指纹(性价比最高)→ 分层 TTL → Stale-While-Revalidate → 预热刷新;不做文件指纹,所有优化手段事倍功半
- 动态加速:专线 + 私有协议(QUIC、私有 TCP 栈)优化回源路径
- 安全兜底:DDoS + WAF + 防盗链(URL 鉴权优先,Referer 校验不靠谱)
面试话术示例: "CDN 本质是一个用空间换时间的分布式缓存系统,核心挑战是调度准确性和缓存可见性——既要让用户请求落到正确的节点,又要保证缓存更新后全网一致。最容易踩的坑有三个:回源风暴(Origin Shield 解决)、DNS 缓存污染(302 兜底)、刷新 API 时序问题(版本化目录替代手动刷新)。"
参考
参考:AWS CloudFront 架构白皮书、Cloudflare CDN 文档、阿里云 CDN 技术文档、Akamai 边缘计算架构、字节跳动边缘云架构分享