图 1:本文封面,命中与回源两条路径。
先说明属性:这是一篇历史笔试题的复盘,题目出自本地历史笔试资料,与任何公司的当前招聘无关。「什么是 CDN」看起来像送分题,但能答到第二层——调度怎么把用户导到最近的节点、缓存过期后会发生什么——才算真懂。这道题拆开就是四块组件加两条路径。
CDN 是什么:把内容搬到离用户近的地方。
没有 CDN 时,全国用户都直接访问你的源站:离得远的时延高,热门资源还把源站带宽打满。CDN(内容分发网络)的思路是在各地部署边缘节点,把图片、JS、CSS、安装包这类静态资源预先或按需缓存到离用户近的节点上。IETF 的 CDN 互联框架文档(RFC 7336)把直接给用户递内容的节点称为 surrogate,我们平时就叫它边缘节点。整个体系四块组件:调度系统负责选节点,边缘节点负责缓存与分发,回源链路负责兜底,源站是内容的权威来源。
调度:用户怎么找到「最近的节点」。
最常用的调度方式是 DNS 重定向。你的资源域名通过 CNAME 指到 CDN 服务商的调度域,用户解析域名时,CDN 的权威 DNS 根据来访解析器的网络位置,返回一个离用户近、负载又合适的边缘节点 IP——同一个域名,不同城市的用户解析出不同地址。RFC 7336 把这类请求路由分为 DNS 重定向与 HTTP 重定向两种基本形态:前者在解析阶段完成、用户全程无感知,后者先连上节点再 302 跳转。DNS 解析本身的全过程这里不展开,记住调度发生在「域名变成 IP」这一步就够了。
命中与回源:边缘节点的缓存账本。
请求到了边缘节点,只有两种结局。命中:缓存里有这份内容且还没过期,直接返回,时延低、源站完全无感知。未命中:节点回源拉取内容,写进本地缓存,再返回给用户——第一个访问的用户慢一点,后面的都快。缓存能存多久由源站说了算:HTTP 缓存规范(RFC 9111)里,响应头 Cache-Control: max-age=30 就是源站在声明「这份副本 30 秒内是新鲜的」,过期之后节点要回源重新拉取或验证。这正好解释了那个经典问题:为什么你更新了资源,用户端还是旧版——因为旧副本还没过期。
用一段 Node.js 脚本模拟这个账本:
// 模拟 CDN 边缘节点的缓存行为:命中直接返回,未命中回源并写入缓存,max-age 到期后再次回源
const MAX_AGE = 30; // 秒,来自源站响应的 Cache-Control: max-age=30
let cache = null; // { content, storedAt }
let originHits = 0;
function origin() { // 源站:内容的权威来源
originHits++;
return { content: 'app.v3.js', headers: { 'Cache-Control': 'max-age=30' } };
}
function request(t) { // t 秒时用户请求到达边缘节点
const fresh = cache && (t - cache.storedAt) < MAX_AGE;
if (fresh) {
return `t=${String(t).padStart(3)}s 命中缓存(已存 ${t - cache.storedAt}s,未过期)→ 直接返回,时延低,源站无感知`;
}
const res = origin(); // 未命中或已过期:回源
cache = { content: res.content, storedAt: t };
return `t=${String(t).padStart(3)}s ${originHits === 1 ? '未命中' : '已过期'} → 回源拉取(第 ${originHits} 次),写入缓存后返回`;
}
for (const t of [0, 5, 12, 25, 35, 60, 65]) console.log(request(t));
console.log(`---\n7 次请求只回源 ${originHits} 次,其余全部由边缘节点消化`);
实测输出:
t= 0s 未命中 → 回源拉取(第 1 次),写入缓存后返回
t= 5s 命中缓存(已存 5s,未过期)→ 直接返回,时延低,源站无感知
t= 12s 命中缓存(已存 12s,未过期)→ 直接返回,时延低,源站无感知
t= 25s 命中缓存(已存 25s,未过期)→ 直接返回,时延低,源站无感知
t= 35s 已过期 → 回源拉取(第 2 次),写入缓存后返回
t= 60s 命中缓存(已存 25s,未过期)→ 直接返回,时延低,源站无感知
t= 65s 已过期 → 回源拉取(第 3 次),写入缓存后返回
---
7 次请求只回源 3 次,其余全部由边缘节点消化
七次请求只回源三次,其余全被边缘节点消化——这就是 CDN 给源站卸压的直观账本。注意 t=60 时命中的是 35 秒时写入的副本,副本年龄 25 秒仍未过期。
图 2:调度、命中、回源与过期的完整链路。
CDN 不解决什么,以及怎么答完整。
两个边界要心里有数。其一,CDN 主要服务静态与可缓存内容;个性化动态接口没法缓存,CDN 最多帮你优化传输链路,省不掉源站的计算。其二,「等过期」之外还有主动手段:发布新版本后通过控制台刷新(purge)让节点提前作废指定内容,或者在大促前做预热,把内容提前推到节点上——CDN 互联标准里的触发接口(RFC 8007)定义的正是这类预置与清除动作。工程上还常见更省事的办法:给静态资源文件名加内容哈希,新版本就是新 URL,天然绕开旧副本。
回到笔试现场,答题结构建议:先一句话定义——CDN 是把内容缓存到离用户最近的节点、就近分发的分布式缓存网络;再画出四块组件与「命中、回源」两条路径;然后用 Cache-Control 过期机制解释「更新了还是旧版」;最后补刷新、预热与文件名哈希。定义是及格线,回源账本和过期机制是区分度。你排查过「资源更新不生效」的问题吗,最后是等过期、手动刷新,还是给文件改了版本号?欢迎分享你的做法。