返回首页

如何理解 CDN?说说实现原理

问题解析(面试官考察点)

面试官通过此问题主要考察:

  • 理解 CDN 的基本概念和价值
  • 了解 CDN 的工作流程和调度机制
  • 掌握 CDN 缓存策略和优化手段
  • 理解负载均衡和智能调度原理
  • 能够分析 CDN 在实际业务中的应用

核心概念(基础知识点)

什么是 CDN

CDN(Content Delivery Network,内容分发网络)是一种分布式网络架构,通过将网站内容分发到全球各地的边缘节点,使用户能够从最近的节点获取资源,从而提高访问速度和用户体验。

CDN 的核心价值:

价值 说明
加速访问 用户就近获取资源,减少网络延迟
降低源站压力 分担源站流量,减少带宽消耗
提高可用性 多节点冗余,单点故障不影响服务
安全防护 提供 DDoS 防护、WAF 等安全能力

CDN 系统架构

┌─────────────────────────────────────────────────────────────┐
│                        用户请求                              │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│  1. DNS 解析(智能调度)                                      │
│     - 本地 DNS 向 CDN DNS 查询                               │
│     - CDN DNS 返回最优边缘节点 IP                            │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│  2. 边缘节点(Edge Node)                                     │
│     - 检查资源是否缓存                                        │
│     - 命中:直接返回资源                                      │
│     - 未命中:向源站或上层节点回源                            │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│  3. 源站(Origin Server)                                     │
│     - 存储原始内容                                            │
│     - 响应回源请求                                            │
└─────────────────────────────────────────────────────────────┘

CDN 关键术语

术语 说明
边缘节点 最接近用户的缓存服务器,分布在全球各地
源站 原始内容存储服务器,CDN 未命中时回源获取
回源 CDN 节点从源站获取资源的过程
命中率 请求命中缓存的比例,衡量 CDN 效果的重要指标
TTL Time To Live,缓存有效期
CNAME 域名别名,用于将域名指向 CDN 域名

详细解答(代码示例)

DNS 调度流程

用户访问 www.example.com 的流程:

┌─────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  用户   │───►│  本地 DNS   │───►│  CDN DNS    │───►│  权威 DNS   │
└─────────┘    └─────────────┘    └─────────────┘    └─────────────┘
     │              │                  │                  │
     │ 1.查询       │                  │                  │
     │─────────────►│                  │                  │
     │              │ 2.查询 CNAME     │                  │
     │              │─────────────────►│                  │
     │              │ 3.返回 CNAME     │                  │
     │              │◄─────────────────│                  │
     │              │   (example.cdn.com)                 │
     │              │ 4.查询 A 记录    │                  │
     │              │─────────────────►│                  │
     │              │                  │ 5.智能调度       │
     │              │                  │  选择最优节点    │
     │              │ 6.返回边缘 IP    │                  │
     │              │◄─────────────────│                  │
     │ 7.返回 IP    │                  │                  │
     │◄─────────────│                  │                  │
     │              │                  │                  │
     │ 8.向边缘节点发起 HTTP 请求                        │
     │══════════════════════════════════════════════════►│

智能调度算法

// CDN 智能调度考虑因素
const schedulingFactors = {
  // 地理位置
  geoLocation: {
    userIP: '用户 IP 地址',
    nodeLocations: ['节点1-北京', '节点2-上海', '节点3-广州'],
    calculateDistance: (userIP, nodeIP) => {
      // 基于 IP 库计算物理距离
      return distance;
    }
  },

  // 网络运营商
  isp: {
    userISP: '中国电信',
    nodeISPs: ['电信', '联通', '移动', 'BGP'],
    // 同运营商优先
    sameISPBonus: 0.3  // 权重加成
  },

  // 节点负载
  nodeLoad: {
    cpu: 'CPU 使用率',
    bandwidth: '带宽使用率',
    connections: '并发连接数',
    // 负载高的节点降低权重
    loadFactor: (load) => 1 - (load - 0.5) * 0.5
  },

  // 节点健康状态
  health: {
    status: 'healthy | degraded | down',
    lastCheck: '上次检测时间',
    // 不健康节点排除
    available: (node) => node.status === 'healthy'
  },

  // 链路质量
  linkQuality: {
    latency: '延迟(ms)',
    packetLoss: '丢包率',
    jitter: '抖动'
  }
};

// 综合评分算法
function calculateNodeScore(node, user) {
  const distanceScore = 1 / (1 + geoDistance(user, node));
  const ispScore = user.isp === node.isp ? 1 : 0.7;
  const loadScore = 1 - node.load;
  const qualityScore = 1 / (1 + node.latency / 100);

  return (
    distanceScore * 0.3 +
    ispScore * 0.25 +
    loadScore * 0.25 +
    qualityScore * 0.2
  );
}

缓存策略配置

# Nginx CDN 缓存配置示例
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=cdn_cache:100m
                 max_size=10g inactive=60m use_temp_path=off;

server {
    listen 80;
    server_name cdn.example.com;

    location / {
        proxy_pass http://origin.example.com;

        # 启用缓存
        proxy_cache cdn_cache;
        proxy_cache_valid 200 302 10m;   // 200/302 缓存 10 分钟
        proxy_cache_valid 404 1m;         // 404 缓存 1 分钟
        proxy_cache_use_stale error timeout invalid_header updating;

        # 缓存键
        proxy_cache_key "$scheme$request_method$host$request_uri";

        # 添加缓存状态头
        add_header X-Cache-Status $upstream_cache_status;

        # 条件缓存(不缓存带特定 Cookie 的请求)
        proxy_cache_bypass $http_cookie;
        proxy_no_cache $http_cookie;
    }

    # 静态资源长期缓存
    location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
        proxy_pass http://origin.example.com;
        proxy_cache cdn_cache;
        proxy_cache_valid 200 30d;        // 静态资源缓存 30 天
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

缓存刷新 API

// CDN 缓存刷新接口调用示例
const axios = require('axios');
const crypto = require('crypto');

class CDNClient {
  constructor(accessKey, secretKey) {
    this.accessKey = accessKey;
    this.secretKey = secretKey;
    this.endpoint = 'https://cdn.api.example.com';
  }

  // 生成签名
  generateSignature(params) {
    const sortedParams = Object.keys(params).sort().map(key => {
      return `${key}=${params[key]}`;
    }).join('&');

    const stringToSign = `POST&${encodeURIComponent('/')}&${encodeURIComponent(sortedParams)}`;
    return crypto.createHmac('sha1', this.secretKey).update(stringToSign).digest('base64');
  }

  // 刷新 URL 缓存
  async purgeUrls(urls) {
    const params = {
      Action: 'RefreshObjectCaches',
      ObjectPath: urls.join('\n'),
      ObjectType: 'URL',
      Format: 'JSON',
      Version: '2018-05-10',
      AccessKeyId: this.accessKey,
      SignatureMethod: 'HMAC-SHA1',
      Timestamp: new Date().toISOString(),
      SignatureVersion: '1.0',
      SignatureNonce: Math.random().toString(36).substring(7)
    };

    params.Signature = this.generateSignature(params);

    const response = await axios.post(this.endpoint, null, { params });
    return response.data;
  }

  // 预热 URL
  async prefetchUrls(urls) {
    const params = {
      Action: 'PushObjectCache',
      ObjectPath: urls.join('\n'),
      // ... 类似签名逻辑
    };

    const response = await axios.post(this.endpoint, null, { params });
    return response.data;
  }
}

// 使用示例
const cdn = new CDNClient('your-access-key', 'your-secret-key');

// 刷新缓存
cdn.purgeUrls([
  'https://cdn.example.com/style.css',
  'https://cdn.example.com/app.js'
]).then(console.log);

深入理解(原理剖析)

CDN 多级缓存架构

┌─────────────────────────────────────────────────────────────┐
│                        用户层                                │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐                      │
│  │ 浏览器  │  │ 浏览器  │  │ 浏览器  │                      │
│  │ 缓存    │  │ 缓存    │  │ 缓存    │                      │
│  └────┬────┘  └────┬────┘  └────┬────┘                      │
└───────┼────────────┼────────────┼───────────────────────────┘
        └────────────┼────────────┘
                     ↓
┌─────────────────────────────────────────────────────────────┐
│                      边缘层(L1)                            │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐                      │
│  │ 边缘节点 │  │ 边缘节点 │  │ 边缘节点 │  ← 最接近用户       │
│  │ 北京    │  │ 上海    │  │ 广州    │                      │
│  └────┬────┘  └────┬────┘  └────┬────┘                      │
└───────┼────────────┼────────────┼───────────────────────────┘
        └────────────┼────────────┘
                     ↓
┌─────────────────────────────────────────────────────────────┐
│                      区域层(L2)                            │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐                      │
│  │ 区域节点 │  │ 区域节点 │  │ 区域节点 │  ← 省级汇聚         │
│  │ 华北    │  │ 华东    │  │ 华南    │                      │
│  └────┬────┘  └────┬────┘  └────┬────┘                      │
└───────┼────────────┼────────────┼───────────────────────────┘
        └────────────┼────────────┘
                     ↓
┌─────────────────────────────────────────────────────────────┐
│                      中心层(L3)                            │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐                      │
│  │ 中心节点 │  │ 中心节点 │  │ 中心节点 │  ← 全国中心         │
│  │ 北京    │  │ 上海    │  │ 深圳    │                      │
│  └────┬────┘  └────┬────┘  └────┬────┘                      │
└───────┼────────────┼────────────┼───────────────────────────┘
        └────────────┼────────────┘
                     ↓
┌─────────────────────────────────────────────────────────────┐
│                      源站层                                  │
│  ┌─────────────────────────────────────────┐                │
│  │           源站服务器                    │                │
│  │     (原始内容存储)                     │                │
│  └─────────────────────────────────────────┘                │
└─────────────────────────────────────────────────────────────┘

缓存策略:
- L1:高热度内容,TTL 较短
- L2:中等热度,TTL 中等
- L3:全量内容,TTL 较长
- 回源:逐级回源,减少源站压力

动态内容加速

静态内容 vs 动态内容:

静态内容(适合缓存):
- 图片、CSS、JS、视频
- 缓存时间长
- 直接返回缓存副本

动态内容(需要特殊处理):
- API 响应、个性化页面
- 缓存时间短或不缓存
- 使用动态加速技术

动态加速技术:
┌─────────────────────────────────────────────────────────────┐
│  1. 路由优化(Dynamic Route Optimization)                   │
│     - 实时探测网络质量                                       │
│     - 选择最优传输路径                                       │
├─────────────────────────────────────────────────────────────┤
│  2. TCP 优化                                                 │
│     - TCP 连接复用(保持与源站长连接)                        │
│     - TCP 参数调优(窗口大小、拥塞控制)                       │
├─────────────────────────────────────────────────────────────┤
│  3. 边缘计算(Edge Computing)                               │
│     - 在边缘节点执行逻辑                                     │
│     - 减少回源请求                                           │
├─────────────────────────────────────────────────────────────┤
│  4. 智能压缩                                                 │
│     - Gzip/Brotli 压缩                                       │
│     - 减少传输体积                                           │
└─────────────────────────────────────────────────────────────┘

边缘计算(Edge Computing)

// 边缘计算示例:在 CDN 节点执行代码
// 阿里云 EdgeRoutine / Cloudflare Workers

// 边缘处理请求,减少回源
async function handleRequest(request) {
  const url = new URL(request.url);

  // 1. 边缘鉴权
  if (!checkAuth(request)) {
    return new Response('Unauthorized', { status: 401 });
  }

  // 2. 边缘 A/B 测试
  const variant = getVariant(request.headers.get('Cookie'));
  url.searchParams.set('variant', variant);

  // 3. 边缘渲染(ESR - Edge Side Rendering)
  if (url.pathname === '/product') {
    const cachedHtml = await caches.match(url);
    if (cachedHtml) {
      return cachedHtml;
    }

    // 从缓存获取产品数据
    const productData = await PRODUCT_CACHE.get(url.searchParams.get('id'));
    const html = renderProductPage(productData);

    const response = new Response(html, {
      headers: { 'Content-Type': 'text/html' }
    });

    // 缓存渲染结果
    await caches.put(url, response.clone());
    return response;
  }

  // 4. 回源
  return fetch(request);
}

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

最佳实践

1. 缓存策略设计

# 静态资源(长期缓存)
Cache-Control: public, max-age=31536000, immutable
# 配合文件名哈希:app.a3f2b1c.js

# 频繁变化资源(短期缓存)
Cache-Control: public, max-age=300, s-maxage=60
# s-maxage 仅对 CDN 生效

# 私有数据(不缓存)
Cache-Control: private, no-store

# 条件缓存(验证 freshness)
Cache-Control: public, max-age=3600
ETag: "33a64df5"
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT

2. 前端 CDN 优化

<!-- 1. 使用 CDN 加载第三方库 -->
<!-- 推荐:使用知名 CDN,利用浏览器缓存 -->
<script src="https://cdn.jsdelivr.net/npm/vue@3.2.0/dist/vue.global.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.0.0/umd/react.production.min.js"></script>

<!-- 2. 多 CDN 域名并行加载 -->
<script src="https://cdn1.example.com/lib-a.js"></script>
<script src="https://cdn2.example.com/lib-b.js"></script>
<!-- 绕过浏览器单域名并发限制 -->

<!-- 3. DNS 预解析 -->
<link rel="dns-prefetch" href="//cdn.example.com">
<link rel="preconnect" href="//cdn.example.com" crossorigin>

<!-- 4. 资源预加载 -->
<link rel="preload" href="https://cdn.example.com/critical.css" as="style">
<link rel="prefetch" href="https://cdn.example.com/next-page.js">

<!-- 5. 使用 SRI(子资源完整性) -->
<script src="https://cdn.example.com/lib.js"
        integrity="sha384-abc123..."
        crossorigin="anonymous"></script>

3. 性能监控

// CDN 性能监控
function monitorCDNPerformance() {
  // 使用 Resource Timing API
  const resources = performance.getEntriesByType('resource');

  resources.forEach(resource => {
    if (resource.name.includes('cdn.example.com')) {
      const metrics = {
        url: resource.name,
        dns: resource.domainLookupEnd - resource.domainLookupStart,
        tcp: resource.connectEnd - resource.connectStart,
        ttfb: resource.responseStart - resource.requestStart,
        download: resource.responseEnd - resource.responseStart,
        total: resource.responseEnd - resource.startTime,
        // 检查是否命中缓存
        cacheHit: resource.transferSize === 0
      };

      // 上报监控数据
      reportMetrics(metrics);
    }
  });
}

// 检查 CDN 缓存状态
async function checkCacheStatus(url) {
  const response = await fetch(url, { method: 'HEAD' });
  const cacheStatus = response.headers.get('X-Cache-Status'); // HIT / MISS / BYPASS
  const age = response.headers.get('Age'); // 缓存时间

  return { cacheStatus, age };
}

4. 安全防护

# CDN 安全防护配置

# 1. 防盗链
location /protected/ {
    valid_referers none blocked server_names *.example.com;
    if ($invalid_referer) {
        return 403;
    }
}

# 2. IP 黑白名单
location / {
    allow 192.168.1.0/24;
    deny 10.0.0.0/8;
    allow all;
}

# 3. 限流
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
location /api/ {
    limit_req zone=one burst=20 nodelay;
}

# 4. WAF 规则(Web Application Firewall)
# 防护 SQL 注入、XSS 等攻击

面试要点

  1. CDN 核心价值

    • 就近访问,降低延迟
    • 分担源站压力
    • 提高可用性和安全性
  2. 智能调度机制

    • DNS 调度:基于地理位置、运营商
    • HTTP DNS:绕过本地 DNS,更精准调度
    • 302 调度:实时性更高
    • Anycast:基于 BGP 路由调度
  3. 缓存策略

    • 缓存键设计:URL、Query、Cookie、Header
    • 缓存层级:浏览器 -> L1 -> L2 -> L3 -> 源站
    • 缓存刷新:URL 刷新、目录刷新、全站刷新
    • 预热机制:提前将热点内容推送到边缘
  4. CDN 优化手段

    • 动静分离:静态资源走 CDN,动态请求直连
    • 域名分片:增加并行下载数
    • HTTP/2 Server Push:主动推送资源
    • 边缘计算:在边缘节点处理逻辑
  5. 面试高频问题

    • CDN 如何解决跨域问题?(配置 CORS 头部)
    • 什么是 CDN 的命中率?如何提高?
    • CDN 和反向代理的区别?
    • 如何处理 CDN 缓存更新?(版本号、文件名哈希)
    • 什么是边缘计算?应用场景?