Skip to content

设计一个 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 SSD1-10TB1-5ms短期(5min-24h)60%-70%
L2 区域中心SSD + HDD50-500TB10-30ms中期(1-7d)20%-25%
Origin ShieldSSD全量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% 以上。

yaml
# 回源策略配置示例(简化 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 解析阶段做智能调度。

请求流程:

  1. 用户请求 cdn.example.com,递归 DNS 查询到 CDN 的权威 DNS
  2. CDN 的 GSLB 根据用户源 IP 推算地理位置、运营商,结合节点健康状态和负载,返回最优边缘节点的 VIP 地址
  3. 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 万元/年。

命中率优化手段(按优先级排序):

  1. 文件指纹(Content Hash):文件名带 hash(如 app.a1b2c3.js),内容变化时 URL 自然失效,不需要手动刷新。这是性价比最高的手段,零额外成本就能把命中率从 85% 提到 95%+
  2. 分层 TTL:静态资源长期缓存(7-30 天),频变资源短 TTL(5-60s)。不要全站统一 TTL
  3. Stale-While-Revalidate:响应头加 Cache-Control: stale-while-revalidate=120,过期后仍返回旧版本,同时异步回源更新。用户零感知,命中率提升 3-5 个百分点
  4. Range 请求优化:大文件(视频 4K 流)支持分片缓存,部分片段失效不影响其他分片。视频 CDN 的命中率瓶颈通常在小文件(m3u8 索引、缩略图),而不是大文件(ts 分片)
  5. 预热预加载(Preload):大促前提前将商品图片、页面模板推送到全国所有边缘节点。阿里双十一前会预热超过 100TB 的内容到 CDN,确保秒杀 0 点缓存全命中

缓存刷新与预热 API:

bash
# 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 生成)
python
# 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 设计的核心脉络:

  1. 缓存分层:边缘节点 → 区域中心 → Origin Shield → 源站,Origin Shield 防止回源风暴
  2. 智能调度:GSLB 基于地理/运营商/负载/延迟做 DNS 解析调度,Local DNS 缓存污染需要 302 兜底
  3. 命中率运营:文件指纹(性价比最高)→ 分层 TTL → Stale-While-Revalidate → 预热刷新;不做文件指纹,所有优化手段事倍功半
  4. 动态加速:专线 + 私有协议(QUIC、私有 TCP 栈)优化回源路径
  5. 安全兜底:DDoS + WAF + 防盗链(URL 鉴权优先,Referer 校验不靠谱)

面试话术示例: "CDN 本质是一个用空间换时间的分布式缓存系统,核心挑战是调度准确性和缓存可见性——既要让用户请求落到正确的节点,又要保证缓存更新后全网一致。最容易踩的坑有三个:回源风暴(Origin Shield 解决)、DNS 缓存污染(302 兜底)、刷新 API 时序问题(版本化目录替代手动刷新)。"

参考

参考:AWS CloudFront 架构白皮书、Cloudflare CDN 文档、阿里云 CDN 技术文档、Akamai 边缘计算架构、字节跳动边缘云架构分享

手撕 → 框架 → 生产化,一步步把 AI Agent 工程化搞透。