文章

DNS解析与CDN加速原理深入解析

DNS解析与CDN加速原理深入解析

一句话概括

DNS(域名系统)将人类可读的域名解析为机器可读的 IP 地址,是整个互联网的”电话簿”;CDN(内容分发网络)则利用全球分布的边缘节点缓存静态资源,使用户从最近的节点获取内容,大幅降低延迟——两者是高性能 Web 应用的底层基础设施。

背景与意义

为什么需要 DNS?

互联网上的每台服务器都有一个 IP 地址(如 142.250.80.46),但人类记不住 32 位的数字。DNS 充当了翻译器:

1
2
3
4
5
你在浏览器输入:           https://www.example.com
                              ↓
DNS 解析:                   www.example.com → 93.184.216.34
                              ↓
浏览器向 93.184.216.34:443 发起 TCP 连接

没有 DNS 的话: 每个人都要背 IP 地址,服务器换了 IP 没人知道。

为什么需要 CDN?

一个典型的页面加载耗时分布:

1
2
3
4
5
6
7
8
9
10
11
没有 CDN 时:
                    ╔══════════════╗
    DNS 查询         ║  50-200ms   ║
    TCP 连接         ║  30-100ms   ║
    TLS 握手         ║  50-200ms   ║   ← 三次网络交互
    HTTP 请求        ║  50-200ms   ║
                    ╚══════════════╝
    总共:至少 180-700ms 的网络开销

    加上全球物理距离:美国用户访问中国的服务器(约 200ms RTT)
    → 加载一个页面至少 1-2 秒

CDN 的做法:把资源缓存到离用户最近的节点,网络延迟从 200ms 降到 20ms。

面试高频信号

DNS 和 CDN 在前端面试中出现的频率越来越高:

  • “从输入 URL 到页面显示,DNS 做了什么?”
  • “什么是 DNS 缓存?浏览器 DNS 缓存多久?”
  • “CDN 的工作原理是什么?”
  • “CDN 回源是什么意思?”

概念与定义

DNS 解析流程(完整递归)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
用户在浏览器输入 www.example.com
    │
    ├── 1. 浏览器 DNS 缓存查询(记住之前查过的,通常缓存 60 秒)
    │    └─ 未命中 →
    ├── 2. 操作系统 DNS 缓存查询(系统级别缓存)
    │    └─ 未命中 →
    ├── 3. hosts 文件检查(本地静态映射,优先级最高)
    │    └─ 未命中 →
    ├── 4. 本地 DNS 服务器查询(ISP 或 8.8.8.8 等公共 DNS)
    │    ├─ 有缓存 → 返回结果
    │    └─ 无缓存 →
    │         ├─ 问根服务器("."):"www.example.com 在哪儿?"
    │         │    → "去问 .com 的顶级域服务器"
    │         ├─ 问 TLD 服务器(".com"):"www.example.com 在哪儿?"
    │         │    → "去问 example.com 的权威服务器"
    │         └─ 问权威服务器:"www.example.com 在哪儿?"
    │              → "答案是 93.184.216.34"
    │
    └── 5. 返回 IP 地址 → 浏览器发起 TCP 连接

几个关键时间

  • 本地 DNS 缓存:约 1-10ms
  • 完整递归解析:约 30-200ms(取决于缓存命中率)
  • 全球平均 DNS 解析时间:约 50-80ms

CND 工作原理

1
2
3
4
5
6
7
8
9
10
用户访问 https://cdn.example.com/style.css
    │
    ├── 1. DNS 解析 cdn.example.com
    │    └─ 权威 DNS 使用 CNAME 或智能 DNS,返回最近的 CDN 边缘节点 IP
    │
    ├── 2. 用户连接到最近的 CDN 边缘节点(Edge Server)
    │    ├─ 节点有缓存 → 直接返回(缓存命中)
    │    └─ 节点无缓存 → 回源(去源站拉取资源)→ 缓存到本地 → 返回给用户
    │
    └── 3. 后续同区域的用户访问同一资源 → 直接从边缘节点返回(缓存命中)

最小示例

1
2
3
4
5
6
7
8
9
10
11
12
13
# DNS 查询命令
# 查看域名解析的 IP
nslookup www.example.com

# 查看完整 DNS 解析路径
dig www.example.com +trace

# 查看 CDN 效果:从不同地区访问
curl -H "Accept-Encoding: gzip" -o /dev/null -s -w 'Time: %{time_total}s\n' \
  https://cdn.example.com/file.js

# 查看服务器 IP 所在地区
curl https://ipinfo.io/93.184.216.34
1
2
3
<!-- CDN 加速静态资源 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">

核心知识点拆解

知识点 1:DNS 记录类型

记录类型含义示例
A域名 → IPv4example.com → 93.184.216.34
AAAA域名 → IPv6example.com → 2606:2800:220:1:248:1893:25c8:1946
CNAME域名 → 另一个域名(别名)www.example.com → example.cdn.cloudflare.net
MX邮件服务器@ → mail.example.com
TXT文本记录(SPF/DKIM 验证)v=spf1 include:_spf.google.com ~all
NS域名服务器example.com → ns1.example.com
SOA权威信息(管理信息)包含主服务器、管理员邮箱、刷新间隔等

💡 CDN 的核心:CDN 通过 CNAME 将你的域名指向 CDN 提供的域名,CDN 的权威 DNS 再根据用户 IP 返回最近的边缘节点 IP。

知识点 2:DNS 缓存层级与 TTL

1
2
3
# DNS 响应中的 TTL(Time To Live)
www.example.com.  3600  IN  A  93.184.216.34
                   ↑ TTL = 3600 秒(1 小时)

缓存层级

1
2
3
4
5
6
7
浏览器缓存     ← TTL 范围内命中 ,约 60 秒
    ↑
操作系统缓存   ← TTL / 2 的规则
    ↑
本地 DNS 缓存  ← 完全遵守 TTL
    ↑
根/顶级/权威   ← 中间 DNS 的结果也缓存

TTL 的最佳实践

  • 静态资源(js/css/img):长 TTL(86400 秒 = 1 天)
  • 需要切换 CDN 时先降低 TTL:短 TTL(300 秒)
  • 切换完成后再恢复长 TTL

知识点 3:CDN 的核心技术

1. 内容路由(智能 DNS)

1
2
3
用户在上海 → DNS 返回上海节点(延迟 5ms)
用户在纽约 → DNS 返回纽约节点(延迟 8ms)
用户在伦敦 → DNS 返回伦敦节点(延迟 7ms)

CDN 根据用户的 EDNS Client Subnet(ECS) 或 DNS 请求的源 IP 判断用户位置,返回最近的边缘节点。

2. 缓存策略

1
2
3
4
# 源站设置的缓存控制头部
Cache-Control: public, max-age=31536000   # CDN 缓存 1 年
Cache-Control: s-maxage=86400              # 仅 CDN 缓存 1 天(覆盖 max-age)
CDN-Cache-Control: public, max-age=3600   # 部分 CDN 支持的专用头部

3. 回源(Origin Pull)

当边缘节点没有缓存时,它会向源站请求资源。回源策略:

1
2
3
4
5
6
7
8
9
回源方式:
├── 直接回源:请求直接转发到源服务器
├── 分层回源:先问上层缓存节点 → 上层未命中 → 问源站
└── 预缓存:提前将热点资源推送到边缘节点

回源优化:
├── 回源超时:5-30 秒
├── 回源重试:失败后重试 2-3 次
└── 回源降级:回源失败时返回过期的缓存内容(stale-while-revalidate)

4. 动态加速(DCDN)

动态内容(API 请求)也可以通过 CDN 加速,核心是优化网络路径而非缓存:

1
2
3
4
用户 → 边缘节点(最近的) → CDN 内部优化路由 → 源站
                                           ↑ BGP 优化路由
                                           ↑ TCP 优化(多路径)
                                           ↑ TLS 复用

知识点 4:前端开发中的 DNS 优化技巧

使用 DNS-Prefetch

1
2
3
4
5
6
7
8
9
10
<head>
  <!-- 告诉浏览器提前解析域名 -->
  <link rel="dns-prefetch" href="//api.example.com">
  <link rel="dns-prefetch" href="//cdn.example.com">
  <link rel="dns-prefetch" href="//images.example.com">
  
  <!-- preconnect 比 dns-prefetch 更强:DNS + TCP + TLS 预连接 -->
  <link rel="preconnect" href="https://api.example.com">
  <link rel="preconnect" href="https://cdn.example.com" crossorigin>
</head>
1
2
3
4
5
6
7
8
9
<!-- 浏览器行为对比 -->
dns-prefetch: 只做 DNS 查询
preconnect:   DNS 查询 + TCP 连接 + TLS 握手
prefetch:     DNS + TCP + TLS + 下载资源(浏览器空闲时)

场景选择:
- 只用 DNS:dns-prefetch(最轻量)
- 确定会访问:preconnect(中度)
- 确定会下载:prefetch(较重)

实战案例

案例一:阿里云 CDN + OSS 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Nginx 源站配置:配合 CDN
server {
    listen 443 ssl;
    server_name static.example.com;
    
    root /var/www/static;
    
    # 静态资源缓存策略
    location ~* \.(js|css|png|jpg|webp|svg|woff2)$ {
        expires 365d;
        add_header Cache-Control "public, immutable, max-age=31536000";
        # 告诉 CDN 文件版本号(用于强制刷新)
        add_header X-Cache-Key $uri;
    }
    
    # HTML 文件不缓存
    location ~* \.html$ {
        expires -1;
        add_header Cache-Control "no-cache, must-revalidate";
    }
}
1
2
3
4
5
6
// 前端资源引用加版本号
const ASSET_URL = 'https://cdn.example.com';
const version = 'v1.2.3';

// 版本号在文件名中(利于 CDN 缓存)
const scriptSrc = `${ASSET_URL}/js/app.${version}.min.js`;

案例二:使用 Service Worker 实现离线缓存(CDN 的补充)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Service Worker:CDN 缓存兜底
self.addEventListener('fetch', (event) => {
  const request = event.request;
  
  // CDN 资源优先从网络获取(保持最新)
  if (request.url.includes('cdn.example.com')) {
    event.respondWith(
      fetch(request)
        .then(response => {
          const cloned = response.clone();
          caches.open('cdn-cache').then(cache => {
            cache.put(request, cloned);
          });
          return response;
        })
        .catch(() => {
          // CDN 不可用时使用缓存
          return caches.match(request);
        })
    );
  }
});

底层原理

DNS 的分布式设计

DNS 系统不是一台服务器,而是一个全球分布式数据库

1
2
3
4
5
6
7
根 DNS 服务器(13 组,全球 1000+ 实例)
  ↓(委派)
顶级域(TLD)服务器(.com / .org / .cn 等)
  ↓(委派)
权威 DNS 服务器(注册商或自建的 DNS 服务器)
  ↓(记录)
最终域名 → 对应的 IP 地址

🎯 关键 insight:DNS 的”分层”设计让它能承受全球每天数万亿次的查询。没有中心化瓶颈。

CDN 的核心指标

指标含义
命中率缓存命中/总请求> 90%< 70%
回源率未命中/总请求< 10%> 30%
P99 延迟99% 请求在 X 毫秒内< 50ms> 200ms
可用性服务正常运行时间99.99%+< 99.9%

高频面试题解析

Q1:从输入 URL 到页面显示,DNS 做了什么?

参考答案:

  1. 浏览器 DNS 缓存 → 未命中
  2. 操作系统 DNS 缓存 → 未命中
  3. hosts 文件检查 → 未命中
  4. 本地 DNS 服务器发起递归查询:
    • 根服务器 → .com 顶级域服务器
    • 顶级域服务器 → 域名的权威服务器
    • 权威服务器 → 返回 IP
  5. 结果缓存到各级 DNS,返回 IP 给浏览器
  6. 浏览器开始 TCP 连接

Q2:什么是 CDN 回源?什么情况下会回源?

参考答案: 回源指边缘节点没有缓存时,向后端源站请求资源的过程。触发的场景:

  1. 资源从未被请求过(冷启动)
  2. TTL 过期(缓存失效)
  3. 源站主动刷新了缓存(手动清除)
  4. 动态内容不缓存

回源是会增加延迟的,所以高命中率是 CDN 的核心指标。

Q3:dns-prefetch 和 preconnect 的区别?

参考答案:

  • dns-prefetch:只做 DNS 解析(最轻量)
  • preconnect:DNS 解析 + TCP 连接 + TLS 握手(更重但更彻底)
  • 高效做法:对一定会访问的域名用 preconnect,可能访问的域名用 dns-prefetch

Q4:CDN 缓存刷新(Purge)后,用户什么时候能看到新版本?

参考答案: 取决于 TTL:

  • TTL=1小时:最长1小时后所有用户都能获取新版本
  • 可通过强制刷新(URL 加版本号)绕过 CDN 缓存
  • 也可通过 CDN 管理面板手动清除特定 URL 的缓存

总结与扩展

核心要点

  1. DNS = 分层分布式数据库,域名 → IP 的翻译服务
  2. CDN = 全球分布式缓存 + 智能路由,把内容送到用户近端
  3. DNS 缓存 有浏览器/操作系统/本地 DNS 三层
  4. CDN 核心:缓存命中率、回源策略、智能 DNS 路由
  5. 前端优化:dns-prefetch / preconnect 预解析域名

延伸学习方向

  • HTTP/3 (QUIC):基于 UDP 的传输协议,CDN 加速效果的进一步提升
  • Anycast:CDN 节点通过同一个 IP 对外服务(BGP Anycast)
  • Edge Computing:在 CDN 边缘节点执行 JS 代码(Cloudflare Workers / AWS Lambda@Edge)
  • DoH / DoT:加密 DNS 查询(DNS over HTTPS / DNS over TLS)

相关主题

本文由作者按照 CC BY 4.0 进行授权