9.2 回源策略与回源率
一句话
回源策略决定 “拿不到时向谁要、怎么要、要不到怎么办”,回源率是它的成绩单——回源率从 30% 降到 8%,源站带宽与尾延迟一起下一个台阶;而 “回源风暴” 几乎总是配置问题,不是流量问题。
回源配置:七项,每项都有默认坑
| 配置 | 常见取值 | 配错的后果 |
|---|---|---|
| 回源 HOST | 与加速域名一致 / 源站真实域名 | 写成 IP → 源站证书不匹配(SNI 校验失败),只能降级 HTTP |
| 回源协议 | HTTP / HTTPS / 跟随客户端 | 跟随客户端时会混用协议,源站侧缓存与重定向混乱 |
| 回源端口 | 80 / 443 / 自定义 | 自定义端口忘记在安全组放行 → 全量 502 |
| 连接超时 | 3–5 秒 | 设 60 秒 → 源站慢时连接堆积,边缘线程被占满 |
| 读取超时 | 10–30 秒 | 与大文件下载冲突;下载类要单独放宽 |
| 重试次数 | 1–2 次(只对幂等方法) | 对 POST 重试 → 重复下单 |
| 跟随重定向 | 开 / 关 | 关:源站 302 直接透传给用户;开:边缘替你跳,但可能跳出加速域 |
回源模式:四种,选错会在故障时放大
| 模式 | 怎么做 | 适合 | 风险 |
|---|---|---|---|
| 单源 | 全部回一个源站 | 小站、验证期 | 源站挂 = 全站挂 |
| 主备 | 主源失败切备源 | 迁移期、灾备 | 备源数据滞后,切过去可能读到旧内容 |
| 多源加权 | 按权重/地域分回多个源 | 多机房、异地部署 | 缓存不一致:同一 URL 在两个源内容不同 |
| 源站组 + 健康检查 | 自动剔除不健康源 | 生产推荐 | 健康检查本身会给源站带来额外 QPS |
一条判据:多源的前提是 “内容一致或按地域一致”。如果没有一致性保障,多源只会把 “缓存不一致” 变成随机故障。
回源率:定义、目标与四个降法
回源率 = 回源请求数 / 边缘总请求数
| 内容类型 | 合理回源率 | 说明 |
|---|---|---|
| 静态资源(图片/JS/CSS) | 5%–15% | 低于 5% 往往意味着 TTL 太长或内容不更新 |
| 下载/点播分片 | 10%–30% | 长尾文件多,命中率天然偏低 |
| 动态接口 | 接近 100% | 不可缓存的不该追求低回源率 |
四个降法按性价比排序:
| 手段 | 效果 | 代价 |
|---|---|---|
| 延长 TTL(在可接受的新鲜度内) | 直接降低回源 | 内容更新变慢(靠刷新/版本化补偿,见 9.4 预热、刷新与失效) |
| 修正缓存键(去掉随机参数与无关 Cookie) | 命中率可能翻倍 | 需要梳理业务参数(见 9.3 缓存键设计) |
| 请求合并(single-flight) | 把 “N 个并发 MISS” 压成 1 次回源 | 实现成本;需要锁与超时 |
| 预热主要对象 | 发布瞬间不打源站 | 只对可枚举的对象有效 |
回源风暴:一个 MISS 引发的雪崩
典型链条:刷新/过期 → 大量请求同时 MISS → 每个边缘节点各自回源 → 源站被打满 → 超时 → 重试放大 → 全站不可用。
| 防护 | 做法 |
|---|---|
| 请求合并 | 同一 URL 在边缘只允许一个回源在飞,其余等待共享结果 |
| 回源限速 | 单节点对单源站的并发/速率上限(超出排队或直接返回旧副本) |
| stale-if-error | 回源失败时返回过期副本(短期脏数据好过 5xx) |
| 源站侧过载保护 | 限流 + 队列 + 快速失败,而不是排队到超时 |
| 分散过期 | 给 TTL 加随机抖动(±10%),避免同一秒集体过期 |
指标:四个数就能判断回源是否健康
| 指标 | 健康信号 | 异常含义 |
|---|---|---|
| 回源率 | 稳定在目标区间 | 突升 → 键/TTL/刷新出问题 |
| 回源 QPS | 与命中流量成比例 | 远高于预期 → 合并失效或键碎片化 |
| 回源 5xx / 超时 | < 0.1% | 上升 → 源站过载或回源配置错 |
| 回源 P95 延迟 | 与源站自身 P95 接近 | 明显更高 → 链路绕路或回源 HOST 错配 |
反模式
| 反模式 | 后果 |
|---|---|
| 回源超时设 60 秒 | 源站抖动时边缘连接被占满,故障扩散到全站 |
| 对 POST 开启回源重试 | 重复提交/重复扣款 |
| 回源 HOST 填 IP | 证书与 SNI 不匹配,只能退回 HTTP |
| 多源但没有一致性保障 | 同一 URL 在不同节点返回不同内容 |
| 把 “回源率低” 当唯一目标 | TTL 过长导致内容陈旧,用户看到旧版本 |
| 刷新后不打预热 | 刷新瞬间全量 MISS,源站被自己的发布打挂 |
| 没有回源限速 | 一个热点对象就能打满源站出口 |
三条铁律
🔑 回源是 “最后一道容量闸”:源站的容量要按 “最坏情况下的回源率” 设计,不是按平均。
🔑 合并 + 限速 + stale-if-error,三者合起来才防得住回源风暴。
🔑 多源的前提是内容一致;否则你只是把故障从 “源站挂” 换成 “随机脏数据”。
自检
Q1 一次发布后源站 CPU 打满,CDN 回源率从 8% 飙到 60%。最可能发生了什么?
▸ 答案:刷新/新版本导致全量 MISS,且没有预热或合并。新文件名(或刷新后的 URL)在边缘全部未命中,大量请求同时回源;若回源还没有请求合并,一个 URL 的 N 个并发会变成 N 次回源。修法:发布前预热、开请求合并、回源限速,并给 TTL 加抖动避免集体过期。
Q2 回源超时应该设多少?为什么不是越大越好?
▸ 答案:连接超时 3–5 秒、读取超时 10–30 秒(下载类单独放宽)。设大(如 60 秒)的问题是边缘资源被慢请求占住:源站一抖动,所有边缘连接都卡在等待上,正常请求也排队,故障从 “源站慢” 放大成 “边缘不可用”。超时是保护边缘的手段,不是给源站留面子。
Q3 为什么 “同一个 URL 配置了两个源站” 会出随机问题?
▸ 答案:因为两个源站的内容可能不同步(发布只推了一台、缓存没清、数据库主从延迟)。用户这次命中 A、下次命中 B,看到的内容跳变;更糟的是边缘缓存会把两个版本分别缓存,问题持续存在。多源必须先保证 “内容一致或按规则一致”,否则应先修一致性再谈多源。
Q4 什么情况下回源率高是正常的?
▸ 答案:动态接口。登录、下单、查询个人订单这类内容不可缓存,回源率接近 100% 是设计如此。此时要优化的是 “回源链路” 而不是 “命中率”——用长连接、协议优化、就近回源(见 9.5 动态加速)。把动态请求也缓存,是另一类事故(用户看到别人的数据)。
Q5 stale-if-error 会不会让用户长期看到旧内容?
▸ 答案:不会,只要配好边界:它只在 回源失败 时生效,且有 stale-while-revalidate / stale-if-error 的最大时长(如 60 秒–5 分钟)。代价是 “故障期间可能看到几分钟前的数据”,收益是 “源站抖动时用户仍能打开页面”。对价格、库存这类强一致内容,应关闭或把窗口压到极短,并在应用层做版本校验。
跨篇相关:命中率与回源放大的原理见 9.1 边缘缓存原理;键设计决定回源率的上限见 9.3 缓存键设计;刷新与预热的配合见 9.4 预热、刷新与失效;不可缓存内容的加速见 9.5 动态加速;CDN 的整体组成见 9.0 CDN 全景:它到底是什么;回源流量的计费见 5.6 云成本结构。