3. 从上传到访问:完整链路解析
3.1 文件上传的三种方式
方式一:客户端 → 服务端 → 对象存储(传统模式)
浏览器 → 你的后端服务器 → 对象存储流程:
- 用户选择文件,点击上传
- 文件先上传到你的后端服务器
- 后端接收完整文件后,再转上传到对象存储
- 返回上传结果给用户
优点:
- 实现简单,前后端都好控制
- 可以在后端做文件校验、格式转换
- 敏感操作可以记录日志、做权限校验
缺点:
- 带宽双吃:用户上传占用一次带宽,服务器转传又占用一次
- 服务器压力大:大文件会占用大量内存和 CPU
- 上传慢:相当于多了一道中转,用户感知到的上传时间更长
适用场景:小文件(<10MB)、需要后端处理(如图片压缩、加水印)、内部管理系统。
方式二:客户端直传对象存储(现代推荐)
浏览器 ──────→ 对象存储
↑
后端只签发临时凭证流程:
- 用户选择文件,前端先向后端申请"上传凭证"
- 后端验证用户身份,向对象存储服务申请临时 STS 凭证(带过期时间)
- 后端把临时凭证返回给前端
- 前端拿着凭证,直接上传文件到对象存储
- 对象存储返回上传结果,前端通知后端"上传完成"
优点:
- 上传快:少了中转环节,用户感知速度最快
- 服务器压力小:只处理凭证签发,不处理文件流
- 带宽省:只走一次上传流量
- 安全性高:临时凭证有过期时间,泄露也危害有限
缺点:
- 实现稍复杂,需要理解 STS、签名机制
- 前端需要处理分片上传、断点续传等逻辑
- 跨域(CORS)需要配置
适用场景:大文件上传、用户生成内容(UGC)、需要高并发上传的业务。
方式三:分片上传 + 断点续传(大文件必备)
10GB 视频文件
↓
切分成 1000 个 10MB 的分片
↓
并行上传(同时传 5 个分片)
↓
断网了!已传 600 个
↓
恢复网络,从第 601 个继续传
↓
所有分片传完,发起"合并"请求为什么需要分片?
| 场景 | 不分片 | 分片 |
|---|---|---|
| 网络波动 | 传了 99% 断网,全部重传 | 只重传失败的分片 |
| 上传速度 | 单线程,速度慢 | 多线程并行,速度快 |
| 内存占用 | 需要缓存整个文件 | 只需缓存当前分片 |
| 进度显示 | 只有 0% 和 100% | 精确到每个分片的进度 |
主流云厂商的分片规格:
| 厂商 | 分片大小限制 | 最大分片数 | 最小分片大小 |
|---|---|---|---|
| 阿里云 OSS | 100MB | 10000 | 100KB |
| 腾讯云 COS | 5GB | 10000 | 1MB |
| AWS S3 | 5GB | 10000 | 5MB(推荐) |
| 七牛云 | 100MB | 10000 | 4MB |
3.2 CDN 回源策略详解
什么是"回源"?
CDN 边缘节点缓存了源站的内容,但当:
- 用户请求的内容第一次被访问
- 缓存的内容已过期(TTL 到期)
- 缓存被手动刷新/预热
CDN 节点就需要向源站请求最新内容,这个过程就叫"回源"。
回源的三种模式
| 模式 | 原理 | 适用场景 | 优缺点 |
|---|---|---|---|
| 直接回源 | CDN 节点 → 源站 | 源站有公网 IP,且流量不大 | 简单直接,但源站压力大 |
| 中间源回源 | CDN 节点 → 中间层 → 源站 | 大型网站,多层缓存架构 | 分担源站压力,架构复杂 |
| ** OSS/COS 作为源站** | CDN 节点 → 对象存储 | 静态资源、图片、视频 | 最佳实践,成本低、性能好 |
回源配置实战
场景 1:对象存储作为源站(推荐)
用户访问:cdn.example.com/images/photo.jpg
↓
CDN 边缘节点(北京)
↓
未命中,回源到源站
↓
源站:bucket-name.oss-cn-beijing.aliyuncs.com
↓
返回图片,CDN 缓存并响应用户关键配置项:
- 源站类型:OSS/COS 域名 或 自定义源站
- 回源协议:HTTP 还是 HTTPS(建议 HTTPS)
- 回源 HOST:访问源站时使用的 Host 头
- 回源 SNI:HTTPS 回源时的服务器名称指示
场景 2:多源站负载均衡
当单个源站扛不住回源压力时,可以配置多个源站:
CDN 边缘节点
├─ 源站 A (权重 50%)
├─ 源站 B (权重 30%)
└─ 源站 C (权重 20%)主备模式:
CDN 边缘节点
├─ 主源站 A (健康时全部流量)
└─ 备源站 B (主源故障时切换)回源带宽 vs CDN 带宽
这里有个容易混淆的概念:
| 指标 | 定义 | 计费关系 |
|---|---|---|
| CDN 下行带宽 | 从 CDN 节点到用户的流量 | 通常按流量计费的 CDN 费用 |
| 回源带宽 | 从源站到 CDN 节点的流量 | 通常对象存储或源站出流量费用 |
省钱技巧:
- 提高 CDN 命中率(让更多请求命中缓存,减少回源)
- 设置合理的缓存时间(TTL)
- 使用预热功能,在用户访问前就缓存热点内容
- 开启"跟随 301/302",避免不必要的回源跳转
3.3 缓存策略配置
缓存键(Cache Key):决定什么算"同一个文件"
CDN 如何判断两次请求是否应该返回同一个缓存副本?靠的就是缓存键。
默认缓存键通常包括:
- URL 路径(不含查询参数)
- 例如:
/images/photo.jpg
问题场景:
用户 A 请求:/images/photo.jpg?w=100&h=100 (100x100 缩略图)
用户 B 请求:/images/photo.jpg?w=800&h=600 (800x600 大图)如果缓存键只包含路径,两张不同尺寸的图片会被认为是同一个文件,导致混乱。
解决方案:自定义缓存键规则
| 规则 | 示例 | 效果 |
|---|---|---|
| 保留指定查询参数 | 保留 w、h | 不同尺寸分别缓存 |
| 保留所有查询参数 | 保留全部 | 完全精确匹配 |
| 忽略特定查询参数 | 忽略 token、timestamp | 带时间戳的 URL 能命中缓存 |
| 包含请求头 | 包含 Accept-Language | 不同语言返回不同内容 |
实战配置示例(阿里云 CDN):
缓存键规则:
- URL 路径:/images/*
- 保留查询参数:w, h, format
- 忽略查询参数:token, timestamp, utm_source缓存时间(TTL):内容"新鲜度"的平衡
TTL(Time To Live)决定了内容在 CDN 节点上缓存多久。设置太短,回源多、成本高;设置太长,内容更新后用户看到旧内容。
按文件类型设置 TTL 的建议:
| 文件类型 | 建议 TTL | 原因 |
|---|---|---|
| HTML 页面 | 0-5 分钟 | 内容频繁更新,需要实时 |
| JS/CSS 文件 | 1 年(配合文件名 hash) | 内容不变,文件名变化即缓存失效 |
| 图片/视频 | 7-30 天 | 更新频率低,可长期缓存 |
| 字体文件 | 1 年 | 几乎不变 |
| API 响应 | 0-5 分钟(视业务) | 数据实时性要求高 |
前端工程化配合 CDN 的最佳实践:
javascript
// webpack/vite 配置
output: {
filename: 'js/[name]-[contenthash:8].js',
chunkFilename: 'js/[name]-[contenthash:8].chunk.js',
}生成的文件名:app-a3f2b1c9.js
- 文件内容变化 → hash 变化 → 新 URL → 自然缓存失效
- 文件内容不变 → hash 不变 → URL 不变 → 长期缓存命中
缓存刷新与预热
手动刷新(应急场景):
当你更新了源站内容,但 CDN 缓存还没过期,用户看到的还是旧内容:
| 刷新类型 | 效果 | 耗时 | 适用场景 |
|---|---|---|---|
| URL 刷新 | 指定 URL 的缓存失效 | 5-10 分钟 | 单个文件更新 |
| 目录刷新 | 指定目录下所有内容失效 | 10-30 分钟 | 批量更新 |
| 全站刷新 | 整个域名的缓存全部失效 | 30 分钟以上 | 紧急回滚 |
重要提醒:刷新只是让缓存失效,下次请求会回源拉取新内容。不要在高峰期大批量刷新,否则可能导致源站被打爆。
预热( proactive 优化):
刷新是被动的(内容已更新),预热是主动的(提前缓存)。
场景:明天上午 10 点要发一篇爆款文章
今晚就提交预热请求:
- URL: https://cdn.example.com/articles/爆款文章.html
- 预热范围:全国所有边缘节点
效果:
明天 10 点用户访问时,内容已经在边缘节点等着了
→ 零回源延迟,秒开体验