第3章 - CDN 内容分发网络
嗨,朋友!
你有没有想过,为什么你在中国访问美国的网站会很慢,但访问淘宝、B站却很快?为什么大公司都要用 CDN?今天我们就来揭开 CDN(Content Delivery Network,内容分发网络) 的神秘面纱!
CDN 不仅是加速网站的利器,也是前端开发、运维工程师必须掌握的重要技术。
🤔 什么是 CDN?
生活中的类比
想象一下,你在成都想买一本书:
没有 CDN:
成都的你 → (寄快递到北京总仓库) → 等待3天 → 收到书 😫
有了 CDN:
成都的你 → (直接去成都本地书店) → 立即买到 😊
(就近原则,速度快!)
CDN 就像是在全国各地开设了许多"分店",用户可以从最近的"分店"获取内容,大大提高了速度。
正式定义
定义
CDN 是一种通过在全球各地部署边缘服务器,将网站内容缓存到离用户最近的节点,从而加速内容传输的网络架构。
核心思想:
- 🌍 就近访问:用户从最近的服务器获取内容
- 💾 内容缓存:静态资源缓存在边缘节点
- 🚀 减轻负载:降低源站服务器压力
- 📡 智能路由:自动选择最佳路径
🏗️ CDN 的工作原理
传统方式 vs CDN 方式
传统方式:
北京用户 ──────────────→ 美国源站服务器
上海用户 ──────────────→ 美国源站服务器 (都要跨洋访问,慢!)
广州用户 ──────────────→ 美国源站服务器
使用 CDN:
北京用户 → 北京CDN节点 ↘
上海用户 → 上海CDN节点 → 美国源站服务器 (只有CDN节点访问源站)
广州用户 → 广州CDN节点 ↗ (用户访问就近节点,快!)
CDN 访问流程详解
假设用户访问 https://cdn.example.com/images/logo.png
第1步:用户发起请求
用户浏览器请求: https://cdn.example.com/images/logo.png
第2步:DNS 解析
浏览器 → 本地 DNS → CDN DNS 服务器
↓
返回最近的 CDN 节点 IP
(比如北京用户返回北京节点 IP)
第3步:访问 CDN 节点
用户 → 北京CDN节点
|
├─ 缓存命中? → 是 → 直接返回 ✅ (最快)
└─ 缓存未命中? → 否 → 回源站获取 → 缓存 → 返回
第4步:内容返回
北京CDN节点 → 用户浏览器 → 显示图片
流程图
┌──────────┐
│ 用户请求 │
└─────┬────┘
↓
┌─────────────┐
│ DNS 解析 │ → 返回最近的 CDN 节点
└─────┬───────┘
↓
┌─────────────┐
│ CDN 节点 │
└─────┬───────┘
↓
缓存命中?
↙ ↘
是 否
↓ ↓
直接返回 回源获取
↓
缓存并返回
🎯 CDN 的核心技术
1. 智能 DNS 调度
CDN 的 DNS 服务器会根据用户的位置、网络状况等因素,返回最优的节点 IP。
调度策略:
- 地理位置: 返回离用户最近的节点
- 网络状况: 选择网络延迟最低的节点
- 负载均衡: 避免某个节点过载
- 健康检查: 排除故障节点
2. 缓存策略
CDN 节点如何决定缓存什么内容,缓存多久?
Cache-Control 响应头:
Cache-Control: max-age=3600 # 缓存1小时
Cache-Control: no-cache # 每次都要验证
Cache-Control: no-store # 不缓存
Cache-Control: public # 可以被CDN缓存
Cache-Control: private # 不能被CDN缓存(只能浏览器缓存)
示例:
HTTP/1.1 200 OK
Content-Type: image/png
Cache-Control: public, max-age=86400
Expires: Thu, 31 Dec 2024 23:59:59 GMT
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
3. 缓存更新机制
问题: 如果源站内容更新了,CDN 缓存还是旧的怎么办?
解决方案:
方案1: 版本号/哈希值(推荐)
<!-- 旧版本 -->
<script src="/js/app.js?v=1.0.0"></script>
<!-- 新版本(URL变了,自动失效) -->
<script src="/js/app.js?v=1.0.1"></script>
或者使用文件哈希:
<script src="/js/app.a3f2d9e1.js"></script>
方案2: 手动刷新缓存
# 阿里云 CDN 刷新
aliyun cdn RefreshObjectCaches --ObjectPath https://cdn.example.com/app.js
# 腾讯云 CDN 刷新
tccli cdn PurgeUrlsCache --Urls '["https://cdn.example.com/app.js"]'
方案3: ETag 验证
# CDN 向源站验证
GET /app.js HTTP/1.1
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
# 如果未修改,源站返回
HTTP/1.1 304 Not Modified
# 如果已修改,返回新内容
HTTP/1.1 200 OK
ETag: "new-hash-value"
🌍 CDN 的节点分布
全球 CDN 节点示意
北美区域:
├─ 美国西海岸 (洛杉矶、旧金山)
├─ 美国东海岸 (纽约、华盛顿)
└─ 加拿大 (多伦多、温哥华)
欧洲区域:
├─ 英国 (伦敦)
├─ 德国 (法兰克福)
└─ 法国 (巴黎)
亚太区域:
├─ 中国 (北京、上海、深圳、成都...)
├─ 日本 (东京、大阪)
├─ 新加坡
└─ 澳大利亚 (悉尼)
国内 CDN 节点覆盖
一线城市: 北京、上海、广州、深圳 (节点最多)
二线城市: 成都、杭州、武汉、西安...
三线城市: 各省会城市
运营商: 电信、联通、移动 (多线路覆盖)
💻 CDN 在前端开发中的应用
1. 静态资源加速
典型使用场景:
<!DOCTYPE html>
<html>
<head>
<!-- CSS 使用 CDN -->
<link rel="stylesheet" href="https://cdn.example.com/css/style.css">
</head>
<body>
<!-- 图片使用 CDN -->
<img src="https://cdn.example.com/images/logo.png" alt="Logo">
<!-- JavaScript 库使用公共 CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<!-- 自己的 JS 文件使用自己的 CDN -->
<script src="https://cdn.example.com/js/app.js"></script>
</body>
</html>
2. 使用公共 CDN 库
常用公共 CDN:
// jsDelivr (推荐,国内速度快)
https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js
https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js
// unpkg
https://unpkg.com/vue@3/dist/vue.global.js
// cdnjs
https://cdnjs.cloudflare.com/ajax/libs/vue/3.3.0/vue.global.min.js
// 国内 CDN
https://cdn.bootcdn.net/ajax/libs/vue/3.3.0/vue.global.min.js
优点:
- ✅ 减少自己服务器带宽
- ✅ 用户可能已经缓存过(加快加载)
- ✅ CDN 节点多,速度快
3. 图片 CDN 优化
图片处理参数:
<!-- 原图 -->
<img src="https://cdn.example.com/photo.jpg">
<!-- 缩略图 (通过 CDN 参数) -->
<img src="https://cdn.example.com/photo.jpg?w=200&h=200&q=80">
<!-- 阿里云 OSS 图片处理 -->
<img src="https://cdn.example.com/photo.jpg?x-oss-process=image/resize,w_200,h_200">
<!-- 七牛云图片处理 -->
<img src="https://cdn.example.com/photo.jpg?imageView2/1/w/200/h/200">
响应式图片:
<picture>
<!-- 小屏幕用小图 -->
<source media="(max-width: 768px)"
srcset="https://cdn.example.com/photo.jpg?w=400">
<!-- 大屏幕用大图 -->
<source media="(min-width: 769px)"
srcset="https://cdn.example.com/photo.jpg?w=1200">
<!-- 默认图片 -->
<img src="https://cdn.example.com/photo.jpg" alt="Photo">
</picture>
4. Webpack 配置 CDN
配置示例:
// webpack.config.js
module.exports = {
output: {
publicPath: 'https://cdn.example.com/', // CDN 域名
},
// 外部依赖(不打包,使用 CDN)
externals: {
vue: 'Vue',
axios: 'axios',
'element-plus': 'ElementPlus'
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
cdn: {
css: [
'https://cdn.jsdelivr.net/npm/element-plus/dist/index.css'
],
js: [
'https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js',
'https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js',
'https://cdn.jsdelivr.net/npm/element-plus'
]
}
})
]
};
HTML 模板:
<!DOCTYPE html>
<html>
<head>
<!-- 插入 CDN CSS -->
<% for (var i in htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>">
<% } %>
</head>
<body>
<div id="app"></div>
<!-- 插入 CDN JS -->
<% for (var i in htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
</body>
</html>
🔧 如何配置和使用 CDN
步骤1: 选择 CDN 服务商
国内主流 CDN:
| 服务商 | 特点 | 适用场景 |
|---|---|---|
| 阿里云 CDN | 节点多,功能全 | 企业应用 |
| 腾讯云 CDN | 性价比高 | 中小企业 |
| 百度云 CDN | 与百度生态集成 | 百度系产品 |
| 七牛云 | 对象存储+CDN | 图片/视频网站 |
| 又拍云 | 专注存储和CDN | 图片/视频网站 |
| 网宿科技 | 老牌CDN | 大型企业 |
国际 CDN:
- Cloudflare: 全球覆盖,有免费套餐
- AWS CloudFront: 与 AWS 生态集成
- Fastly: 高性能,实时刷新
步骤2: 配置域名
2.1 添加加速域名
源站域名: www.example.com
CDN 域名: cdn.example.com
源站地址: origin.example.com 或 123.45.67.89
2.2 配置 CNAME
CDN 服务商会给你一个 CNAME 记录:
cdn.example.com.cdn.aliyuncs.com
在 DNS 服务商添加 CNAME 记录:
主机记录: cdn
记录类型: CNAME
记录值: cdn.example.com.cdn.aliyuncs.com
2.3 配置缓存规则
# 图片缓存 30 天
/images/* → Cache-Control: max-age=2592000
# CSS/JS 缓存 7 天
/css/* → Cache-Control: max-age=604800
/js/* → Cache-Control: max-age=604800
# HTML 不缓存
/*.html → Cache-Control: no-cache
# API 不缓存
/api/* → Cache-Control: no-store
步骤3: 上传资源
方式1: 手动上传
# 使用阿里云 OSS 工具
ossutil cp -r ./dist/ oss://my-bucket/
# 使用七牛云工具
qshell qupload config.json
方式2: CI/CD 自动部署
# GitHub Actions 示例
name: Deploy to CDN
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: |
npm install
npm run build
- name: Upload to OSS
uses: manyuanrong/setup-ossutil@v2.0
with:
endpoint: oss-cn-beijing.aliyuncs.com
access-key-id: ${{ secrets.OSS_ACCESS_KEY }}
access-key-secret: ${{ secrets.OSS_SECRET_KEY }}
- name: Deploy
run: |
ossutil cp -r ./dist/ oss://my-cdn-bucket/ --update
- name: Refresh CDN
run: |
aliyun cdn RefreshObjectCaches --ObjectPath https://cdn.example.com/
📊 CDN 性能优化
1. 合理设置缓存时间
// Express.js 设置响应头
app.use('/static', express.static('public', {
maxAge: '1d', // 静态资源缓存 1 天
setHeaders: (res, path) => {
if (path.endsWith('.html')) {
res.setHeader('Cache-Control', 'no-cache'); // HTML 不缓存
} else if (path.match(/\.(jpg|jpeg|png|gif|svg)$/)) {
res.setHeader('Cache-Control', 'public, max-age=2592000'); // 图片缓存 30 天
} else if (path.match(/\.(css|js)$/)) {
res.setHeader('Cache-Control', 'public, max-age=604800'); // CSS/JS 缓存 7 天
}
}
}));
2. 开启 Gzip 压缩
# Nginx 配置
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
gzip_min_length 1000; # 小于 1KB 的文件不压缩
gzip_comp_level 6; # 压缩级别 1-9,6 是性价比最好的
3. HTTP/2 加速
# Nginx 启用 HTTP/2
server {
listen 443 ssl http2;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
}
HTTP/2 优势:
- ✅ 多路复用(一个连接处理多个请求)
- ✅ 头部压缩
- ✅ 服务器推送
4. 图片格式优化
<!-- 使用现代图片格式 -->
<picture>
<!-- WebP 格式(体积小 30%) -->
<source type="image/webp" srcset="photo.webp">
<!-- AVIF 格式(体积更小) -->
<source type="image/avif" srcset="photo.avif">
<!-- 传统格式(兜底) -->
<img src="photo.jpg" alt="Photo">
</picture>
5. 预连接优化
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="https://cdn.example.com">
<!-- 预连接(DNS + TCP + TLS) -->
<link rel="preconnect" href="https://cdn.example.com">
<!-- 预加载关键资源 -->
<link rel="preload" href="https://cdn.example.com/app.js" as="script">
🔍 CDN 故障排查
问题1: CDN 缓存没生效
排查步骤:
# 1. 检查响应头
curl -I https://cdn.example.com/app.js
# 查看是否有 CDN 标识
X-Cache: HIT # 缓存命中
X-Cache: MISS # 缓存未命中
Age: 3600 # 缓存了 1 小时
# 2. 检查 Cache-Control
Cache-Control: public, max-age=86400
解决方案:
- 确认源站设置了正确的 Cache-Control
- 检查 CDN 配置的缓存规则
- 手动刷新 CDN 缓存
问题2: 跨域问题
错误信息:
Access to fetch at 'https://cdn.example.com/api/data' from origin
'http://localhost:3000' has been blocked by CORS policy
解决方案:
// Express.js 源站配置
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
或在 CDN 配置中添加 CORS 头。
问题3: 内容未更新
排查:
# 检查文件 ETag
curl -I https://cdn.example.com/app.js | grep ETag
# 对比源站和 CDN 的 ETag
curl -I https://origin.example.com/app.js | grep ETag
解决:
- 使用版本号:
app.js?v=1.0.1 - 手动刷新 CDN 缓存
- 使用文件哈希:
app.a3f2d9e1.js
💡 学习建议
1. 理解缓存机制 🧠
深入理解 HTTP 缓存头:
Cache-ControlExpiresETagLast-Modified
2. 实践为主 💻
- 注册一个免费的 CDN 服务(如 Cloudflare)
- 部署一个静态网站到 CDN
- 对比使用 CDN 前后的加载速度
3. 监控和优化 📊
- 使用浏览器开发者工具查看资源加载
- 使用 GTmetrix、WebPageTest 测试网站性能
- 关注 CDN 命中率、带宽使用情况
📝 练习题
基础题
- CDN 的主要作用是什么?它解决了什么问题?
点击查看答案
主要作用:
- 加速访问: 用户从最近的节点获取内容,减少延迟
- 减轻源站压力: 大部分请求被 CDN 分担
- 提高可用性: 多节点容灾
- 降低带宽成本: 减少源站带宽消耗
解决的问题:
- 跨地域访问慢
- 源站带宽和性能瓶颈
- 高并发场景下的稳定性
- 网络抖动和故障的影响
- CDN 的访问流程是怎样的?
点击查看答案
- 用户请求: 浏览器请求资源
- DNS 解析: CDN DNS 返回最近节点 IP
- 访问 CDN 节点:
- 缓存命中 → 直接返回
- 缓存未命中 → 回源获取 → 缓存 → 返回
- 内容返回: 用户收到资源
进阶题
- 如何解决 CDN 缓存更新的问题?列举至少三种方案。
点击查看答案
方案1: 版本号/哈希值(推荐)
<!-- 修改 URL,自动失效旧缓存 -->
<script src="/app.js?v=1.0.1"></script>
<script src="/app.a3f2d9e1.js"></script>
优点: 最可靠,前端控制 缺点: 需要修改 HTML
方案2: 手动刷新缓存
aliyun cdn RefreshObjectCaches --ObjectPath https://cdn.example.com/app.js
优点: 灵活 缺点: 需要手动操作,有时间延迟
方案3: 短缓存时间
Cache-Control: max-age=300 # 只缓存 5 分钟
优点: 自动更新 缺点: CDN 效果打折扣
方案4: ETag 验证
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Cache-Control: must-revalidate
优点: 自动检测更新 缺点: 仍需请求验证
- 在前端项目中如何配置 CDN?写出 Webpack 配置示例。
点击查看答案
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'production',
output: {
filename: 'js/[name].[contenthash:8].js',
publicPath: 'https://cdn.example.com/', // CDN 域名
clean: true
},
// 外部依赖(使用 CDN,不打包)
externals: {
vue: 'Vue',
'vue-router': 'VueRouter',
axios: 'axios'
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
cdn: {
css: [
'https://cdn.jsdelivr.net/npm/element-plus/dist/index.css'
],
js: [
'https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js',
'https://cdn.jsdelivr.net/npm/vue-router@4/dist/vue-router.global.prod.js',
'https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js'
]
}
})
]
};
<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
<% for (var i in htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>">
<% } %>
</head>
<body>
<div id="app"></div>
<% for (var i in htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
</body>
</html>
🎓 下一步学习
恭喜你!现在你已经掌握了 CDN 的核心概念和实践技巧。
建议继续学习:
- 第4章 - VPN 与代理 - 了解网络访问控制
- 第2章 - 负载均衡原理 - CDN 也是一种负载均衡
- 实战案例 - 前端开发中的网络应用 - CDN 在实际项目中的应用
推荐阅读:
- HTTP 缓存机制详解
- 各大 CDN 服务商官方文档
- Web 性能优化最佳实践
关键概念回顾:
- ✅ CDN 通过就近访问和内容缓存加速网站
- ✅ 智能 DNS 调度返回最优节点
- ✅ 缓存策略通过 Cache-Control 等响应头控制
- ✅ 缓存更新推荐使用版本号/哈希值方案
- ✅ 前端项目可以通过 Webpack 配置 CDN
- ✅ 合理使用 CDN 可以大幅提升网站性能
