前段时间帮一位做独立作品集站点的朋友看他的首屏加载服务器配置不低代码也没多少三方库但打开首页要 3 秒多图片是一张一张往下“蹦”出来的。我让他把静态资源接上一层内容分发网络同一台源站、同一份代码、同一个网络环境首屏直接掉到 0.9 秒左右。他当时问了我一句特别典型的话“这东西到底是把我文件复制到哪儿去了会不会内容不同步”这篇就围绕CDN内容分发网络讲透它本质上是什么、一次请求在它内部走了哪条路径、配置的时候那些开关为什么必须那么开、线上最容易炸的几类事故怎么排查以及账单上的数字是怎么被算出来的。写给出身前端、后端、运维、独立开发的朋友都行静态站点、图片站、下载站、视频点播这类以内容分发为主的场景收益最直接如果你的业务是纯接口型、用户全在一个城市看完你也能判断出“我到底该不该上”。1. 先把 CDN 这个词拆开看它到底缓存了什么、没缓存什么很多人对 CDN 的第一印象是“一个分布在各处的网盘我把文件传上去用户就近下载”。这个理解偏差挺大会直接导致后面配置踩坑。CDN 不是网盘它是一个按需填充的边缘缓存层你不主动上传任何东西边缘节点上有什么内容完全由“第一个访问的用户”决定。打个生活化的比方。连锁便利店不会把总仓的所有货都摆到每个门店而是门店先摆少量高频商品某个顾客要买冷门货店员打电话让总仓送过来送一次之后门店就留几份在货架上。CDN 的边缘节点就是门店源站就是总仓那个“打电话让总仓送”的动作叫回源。这个模型决定了三件事。第一第一次访问一定不快因为要回源边缘节点上还没货。第二冷门内容的缓存会被挤掉节点存储有限长时间没人访问的资源会被淘汰下次再来又是回源。第三源站永远是最终事实来源边缘节点上的副本只是投影投影对不对取决于你的缓存规则写得对不对。所以配置 CDN 的核心工作其实只有两件告诉它“什么东西可以留、留多久”以及告诉它“留不住的时候去哪儿取”。剩下的调度、传输优化、证书管理都是服务商替你干的部分。1.1 从浏览器输入网址到最后一个字节一次请求的完整链路把一次典型的静态资源请求拆开看心里有张链路图排查问题时才不至于乱猜。假设用户要请求https://static.example.com/assets/index.a3f9c2.js。第一步浏览器做 DNS 解析。这个域名在接入 CDN 时已经被配置成 CNAME 指向服务商提供的调度域名于是解析请求最终落到调度系统手里。调度系统根据请求来源、节点健康状态、各节点的负载情况返回一个边缘节点的 IP。第二步浏览器和这个边缘节点建立 TCP 连接完成 TLS 握手。注意这一段是用户到边缘节点不走源站所以延迟取决于用户到节点的物理距离而不是用户到源站的距离——这就是 CDN 收益的主要来源。第三步边缘节点收到 HTTP 请求先算出一个缓存键拿这个键去本地查。查到且未过期直接把内容返回响应里通常会带上Age、X-Cache: HIT这类头这一趟叫命中。整个过程可能只有几十毫秒源站完全不知情。第四步如果没查到或者已经过期边缘节点向源站发起回源请求拿到内容后一边返回给用户一边按规则写入本地缓存。这一趟的耗时等于用户到节点 节点到源站 源站处理时间通常就是“第一次慢、后面快”的原因。第五步如果同一个资源在极短时间内被大量请求边缘节点通常会把并发的回源请求合并成一次只让第一个请求真正打到源站其余请求挂起等待结果。这个机制叫请求合并或回源收敛它是防止源站被打挂的第一道闸门但很多默认配置下并不开启需要手动确认。把这条链路记住后面所有排查都能往里套是调度给错了节点是缓存键算错了导致一直不命中是回源太慢还是证书、跨域这些非缓存因素卡住了位置不同解法完全不同。1.2 CDN 真正卖的不是带宽是“距离”和“并发出口”很多人以为 CDN 主要是省带宽钱。带宽确实能省但真正的价值在时延。光在光纤里的传播速度大约是每秒 20 万公里听起来快得离谱但换算成实际距离就很具体了1000 公里的物理距离单程 5 毫秒一个来回 10 毫秒。这 10 毫秒只是“光跑一趟”的成本还没算中间的设备转发、排队。现在算一笔更直观的账。用户到源站 1000 公里RTT 算 20 毫秒实际公网往往更高。TCP 握手 1 个 RTTTLS 1.3 握手 1 个 RTT这就是 40 毫秒还没开始传一个字节。假设首屏有 40 个资源走 HTTP/1.1 每域名 6 个并发连接要分 7 批每批都要经历建连和慢启动光建连成本就是 280 毫秒起步。换成 HTTP/2 复用一条连接建连只做一次40 毫秒解决。如果用户到边缘节点只有 50 公里RTT 算 5 毫秒同样的握手流程 10 毫秒搞定。这中间省下的不是带宽费是用户感知的“卡不卡”。首屏时间对转化率的影响做前端的都清楚1 秒和 3 秒是两个世界。除了距离还有并发出口能力。源站通常只有一条或几条出口链路1000 个用户同时拉一个 2MB 的图片包源站出口瞬间被打满后面所有请求排队。CDN 把这份并发压力摊到几十上百个节点上每个节点只承担很小一部分源站看到的回源请求可能只有个位数。这就是“突发流量扛得住”的真实原因——不是节点带宽无限而是压力被地理分散了。顺便说一句很多人把 CDN 当成“加速器”觉得接上就一定变快。实际不是。如果你的用户和源站本来就在同一个城市、同一个机房隔壁接 CDN 反而可能变慢因为多了一跳调度和一次额外的连接建立。判断标准很简单用户和源站的平均 RTT 是否显著大于用户到最近节点的 RTT。小于 20 毫秒基本没必要。1.3 动态请求能不能上 CDN边缘计算带来的一点点改变静态资源好理解那接口请求呢比如/api/user/profile每个人返回的数据都不一样缓存了就是事故。默认情况下这类请求是不缓存、直接回源的CDN 在中间只起到“转发 链路优化”的作用。这个“转发”其实也有价值。边缘节点到源站之间往往走的是服务商自己的骨干链路路由质量比公网随机路径好握手也可以复用长连接不必每个用户都跟源站重新建连。所以哪怕是纯动态接口接进来之后 TTFB 也常有改善尤其是跨地域访问的场景。这部分通常叫动态加速计费口径和静态流量不一样配置前要问清楚。再往前一步是边缘函数也就是把一小段逻辑跑在节点上。典型用途是给 HTML 注入灰度标记、做 A/B 分流、根据 UA 做重定向、在边缘校验签名、把多个接口的响应在边缘拼装。这类能力确实扩展了 CDN 的边界但我的建议是能用规则解决的不要写代码。边缘函数有冷启动、有执行时长限制、有调试困难的问题一个跑在几百个节点上的函数出了 bug回滚速度和定位难度都比普通服务高一个量级。判断一个请求该不该走缓存我习惯用三个问题过一遍响应内容是否与用户身份强相关是否带Set-Cookie是否是 POST/PUT 这类非幂等方法三个里有一个“是”就别缓存。反过来只要内容是“同一份给所有人”的哪怕它是接口返回的 JSON也完全可以缓存——很多商品详情、配置下发、地区列表这类接口缓存收益非常可观。2. 一次回源背后的调度逻辑DNS、智能解析与节点选择链路走通之后下一个问题就来了调度系统凭什么判断该把你分到哪个节点这个判断做错了后面所有优化都白搭。我见过最典型的情况是用户在南方被调度到了北方的节点首屏反而比不接 CDN 更慢然后得出“CDN 没用”的结论。2.1 调度系统怎么知道你离哪个节点近最基础的方式是基于LocalDNS 的出口 IP做地理库匹配。你的递归解析服务器在哪个城市就认为你在哪。这个假设在早年还算靠谱现在越来越不准。原因是用公共 DNS 的用户很多递归服务器的出口可能集中在几个大城市你人在三线城市出口 IP 却显示在一线于是被调度到几百公里外的节点。改进方案是EDNS Client Subnet让递归服务器在查询时带上用户的网段前缀调度系统拿真实网段去匹配。这个机制需要递归服务器支持覆盖率不是百分之百所以实际效果是“能拿到就用拿不到就退化”。还有一类方案是主动探测客户端或 SDK 先向若干候选节点发小包测 RTT选最快的那个。这种方式准但需要客户端配合纯浏览器场景做不了。浏览器能做的只有preconnect、dns-prefetch这类提示粒度很粗。最后是Anycast同一个 IP 广播到多地靠路由协议把包送到最近的一份。它省掉了 DNS 这一层判断天然就近但代价是路由收敛期间可能出现短暂抖动而且对“节点级精细调度”不太友好。实际生产里这几套通常是混用的。我自己的经验是接入之后一定要用不同地区的探测点实测一遍看解析结果是否合理。有些服务商的控制台里能直接查“某地用户会被调度到哪个节点”如果没有这个功能就找几个异地的朋友或者用在线多点探测工具跑一下dig比对返回的 IP 归属。这一步花十分钟能省掉后面几天的怀疑人生。提示如果你发现某个地区的解析结果长期指向远端节点先排查是不是 CNAME 配置有残留、或者本地 DNS 缓存没刷新TTL 没到。DNS 的生效延迟是小时级的改完立刻测往往测的是旧记录。2.2 缓存键设计URL、参数与 Vary 头缓存键是 CDN 里最容易写错、后果最隐蔽的一个东西。它决定了“什么算同一个资源”。默认的缓存键通常是协议 主机名 路径 查询字符串。也就是说?v1和?v2会被当成两个完全不同的资源各存一份。这个默认行为在很多场景下是灾难。举个真实例子。有个站点给所有静态资源加了时间戳参数做版本控制app.js?v1717000000每次发版这个数字都变。结果就是每次发版所有用户的第一次访问全部回源边缘缓存里堆着一堆永远不会再被访问的旧版本命中率长期在 60% 上下徘徊。后来改成把哈希写进文件名app.a3f9c2.js查询参数全部从缓存键里剔除命中率直接上到 96%。反过来也有坑某些业务确实需要按参数区分内容比如图片处理服务?w200h200这种参数必须保留在缓存键里否则 200×200 的图会被当成原图返回。所以缓存键不是越简越好是“该留的留、该去的去”。还有一个容易被忽略的是Vary响应头。如果源站返回了Vary: Accept-Encoding那压缩版和非压缩版会被分开缓存这通常是期望行为。但如果返回了Vary: User-Agent缓存维度会爆炸命中率断崖式下跌而且极易出现“A 用户拿到 B 用户的响应”。做响应式图片、多语言按 UA 分流的站点要特别注意这一条。缓存键组成是否建议保留说明协议http/https保留混用会导致重复缓存建议全站强制 https主机名保留多域名指向同一源站时不要合并路径保留核心维度查询字符串版本号类剔除用文件名哈希替代否则命中率暴跌查询字符串图片处理类保留参数直接决定输出内容查询字符串跟踪参数剔除utm_、from、spm 这类对内容无影响Cookie一般不参与参与会导致几乎全部不命中Vary 指定的头谨慎维度超过 3 个就要重新设计配置缓存键的时候我的习惯是先打开日志统计一周内实际出现过的查询参数名按出现频率排序把前几个确认“是否影响内容”剩下的统一剔除。拍脑袋决定往往会漏掉某个业务方偷偷加的参数。2.3 回源策略与缓存层级分层回源省下的不只是带宽单层结构下每个边缘节点未命中都会直接回源。节点多了之后源站看到的回源请求数等于“节点数 × 各自的未命中次数”热门内容刚发布的那几分钟源站压力会非常集中。分层回源也叫中间层回源、父层缓存在边缘和源站之间加一层中间节点。边缘未命中时先问中间层中间层有就直接给没有才回源。这样热门内容在整个网络里只需要回源一两次回源率能从“节点数量级”降到“个位数”。代价是多了一跳首次访问的延迟会增加几十毫秒。所以这个开关要看业务内容更新频繁、节点多、源站扛不住并发的场景开单区域小流量、追求极致首包时间的场景不开。回源本身的几个参数也值得单独说回源协议边缘到源站走 http 还是 https。走 https 更安全但每次回源多一次握手建议开启回源长连接复用。回源 Host这个填错是经典事故。源站上可能挂了多个站点靠 Host 区分。回源 Host 填错源站返回的是另一个站点的内容或者直接 404。默认值通常是加速域名本身但很多源站只认主域名必须手动改。回源超时与重试超时设太短源站稍微慢一点就被判定失败触发重试反而放大压力设太长用户端一直挂着。一般连接超时 5 秒、读取超时 15 到 30 秒是常见区间重试次数 1 到 2 次多了容易形成雪崩。回源失败兜底能不能返回一份过期的旧缓存这个策略叫 stale-if-error用好了能在源站抖动时保住页面可用性用不好会让用户长时间看到过期内容。我的建议是只对静态资源和 HTML 开启接口不开。源站侧的 nginx 配置也顺手贴一下重点是给 CDN 回源留出合理的连接数和缓存头location ~* \.(js|css|png|jpg|jpeg|webp|woff2)$ { root /var/www/static; expires 30d; add_header Cache-Control public, max-age2592000, immutable; add_header X-Source origin-a; access_log /var/log/nginx/static.log; } location /index.html { root /var/www/static; add_header Cache-Control public, max-age0, must-revalidate; }immutable这个指令值得单独提一句它告诉浏览器“这个 URL 对应的内容永远不会变别在刷新时发校验请求了”。前提是文件名里带了内容哈希改了内容文件名就会变。用对了能省掉大量 304 请求用错了就是发版后用户死活看不到新样式。3. 把静态站点接上 CDN一份可直接抄的配置清单理论说够了走一遍完整的接入流程。这套流程我在几个不同类型的站点上重复用过从纯静态博客到带接口的中型应用骨架是一样的差别只在缓存规则的细节。3.1 域名准备与 CNAME 接入的最小闭环准备两个域名角色一个是加速域名对外暴露用户访问的是它一个是源站地址可以是域名也可以是 IP只有 CDN 节点会访问。第一步确认源站能独立访问。直接用源站 IP 加 Host 头测一下curl -I -H Host: static.example.com http://203.0.113.10/assets/index.a3f9c2.js返回 200 且内容正确说明源站这一侧没问题。这一步千万别跳过我遇到过源站防火墙只放行了特定 UA、结果 CDN 回源全被拦的情况排查了半天才发现是源站白名单。第二步在控制台添加加速域名选择业务类型静态资源、下载、点播不同类型默认的缓存策略和计费口径不同填写源站地址和回源 Host。第三步配置 CNAME。控制台会给你一个调度域名去 DNS 服务商把加速域名的 CNAME 指过去。TTL 建议先设短一点比如 600 秒等验证通过再改长。第四步验证。用dig看解析结果是否已经指向调度域名然后用curl打一次加速域名dig static.example.com CNAME short curl -I https://static.example.com/assets/index.a3f9c2.js响应里出现X-Cache: HIT或Age大于 0说明边缘缓存生效了。第一次请求大概率是MISS再打一次看是否变HIT这是最直接的验证方式。第五步灰度。如果是从旧架构迁移别一次性把 DNS 全切。先把加速域名配好用 hosts 或者小范围 DNS 把 5% 流量引过去观察一两天的错误率和回源率再逐步放大。DNS 切换是不可控的一旦出问题回滚也要等 TTL 过期中间那段时间用户看到的就是坏页面。注意如果你的源站在境内域名接入前后会有一些前置校验流程按控制台提示走就行别想着绕。这一步卡住的人不少提前预留时间。3.2 缓存规则怎么排优先级按后缀、目录还是全站缓存规则的匹配通常有优先级精确路径 目录前缀 文件后缀 全站默认。写法上要遵循“从特殊到一般”把最需要精细控制的放在最前面。下面这份表是我在一个内容型站点上实际用的可以直接参考资源类型示例路径边缘 TTL浏览器 TTL说明带哈希的 JS/CSS/assets/index.a3f9c2.js365 天365 天内容变了文件名就变可以永久缓存图片与字体/img/logo.webp30 天7 天图片更新走文件名替换HTML 入口/index.html0不缓存0必须实时否则发版后用户看到旧页面接口/api/*不缓存不缓存带用户态直接回源robots.txt、sitemap/robots.txt1 小时10 分钟更新不频繁但要求较快生效视频分片/video/*.ts7 天7 天分片文件不可变长缓存几个原则值得反复强调。HTML 不要缓存或者只缓存极短时间。这是发版事故的头号来源。HTML 是入口它里面引用的 JS/CSS 文件名是带哈希的只要 HTML 是最新的整站就是最新的。把 HTML 缓存一天等于把所有发版延迟一天。带哈希的文件名可以给超长 TTL。因为内容变化一定伴随文件名变化缓存永远不会“过期错误”。这一类资源是 CDN 收益最大的部分占比通常也最高。不要用查询参数做版本号。前面说过会让缓存键爆炸。如果你现在的构建流程还在用?v时间戳这是我建议优先改造的一件事。接口的缓存要单独评估。有些接口看着是动态的其实是全站共享的配置数据缓存 30 秒收益巨大。有些看着是静态的其实夹杂了用户 ID。判断依据只有一个同一时刻不同用户拿到的响应体是否完全相同。3.3 HTTPS 证书、HTTP/2 与压缩的开关取舍证书这块能在 CDN 侧托管就别自己折腾。上传证书、开启自动续期省心。要注意两点一是证书链要完整缺中间证书在某些安卓机上会直接握手失败而桌面浏览器可能毫无异常这种问题极难复现二是如果源站也走 https回源那一侧的证书校验别关掉关了等于中间这段明文传输。TLS 版本建议只留 1.2 和 1.3。1.0/1.1 已经被主流浏览器放弃留着只会拉低整体安全性评分。开启 OCSP Stapling 能让握手少一次往返收益不大但没成本。HTTP/2 基本是必开的。多路复用能显著减少并发连接数前面算过40 个资源的场景下能省掉几百毫秒的建连开销。HTTP/3 基于 UDP在弱网和移动网络下改善明显代价是部分老旧网络环境可能不通。我的做法是开启 HTTP/3 但保留 HTTP/2 作为回退让客户端自己协商。压缩方面文本类资源HTML、JS、CSS、JSON、SVG必须开。Brotli 比 gzip 通常能再小 15% 到 20%但压缩更耗 CPU。建议在 CDN 侧对静态资源开 Brotli边缘压缩一次缓存起来反复用源站侧只开 gzip 甚至不开把 CPU 压力放到边缘去。图片、视频、字体这些已经压缩过的格式不要再压白费 CPU 还可能导致体积变大。还有一个细节压缩和缓存键的关系。如果同一个 URL 既要返回 br 版本又要返回 gzip 版本缓存键里必须体现Accept-Encoding的差异通常由Vary: Accept-Encoding自动处理。如果手动剔除了这个头可能出现“支持 br 的浏览器拿到了 gzip 内容”虽然不影响正确性但白白损失了压缩收益。3.4 上线后的验证方法用 curl 和响应头确认命中配置完不要只看控制台的图表那玩意儿有延迟而且聚合过了。直接看响应头最准。# 第一次请求大概率 MISS curl -sI https://static.example.com/assets/index.a3f9c2.js | grep -Ei cache|age|via|x- # 第二次请求应该变成 HIT curl -sI https://static.example.com/assets/index.a3f9c2.js | grep -Ei cache|age|via|x-要看的几个头X-Cache或类似的厂商自定义头直接告诉你 HIT / MISS / EXPIRED。Age这份缓存在边缘已经存了多少秒。结合Cache-Control的 max-age 可以算出还剩多久过期。Cache-Control确认边缘有没有改写你源站发出的缓存策略。有些服务商会按自己的规则覆盖。Via能看到经过了哪些中间层。Content-Encoding确认压缩生效。Server-Timing部分地区或配置下能看到回源耗时排查慢请求特别好用。再补一个多地区验证的方法。找个在线的多点探测工具选五六个不同城市的节点同时请求同一个 URL看返回的 IP 和X-Cache状态。正常情况应该是不同地区解析到不同节点热门资源在各节点都是 HIT。如果所有地区都解析到同一个 IP说明调度没生效大概率是 CNAME 没配好或者还在 DNS 缓存期。还有一个必测项发版流程走一遍。改一个文件重新构建上传然后立刻访问。看 HTML 是不是新的、引用的哈希文件名是不是新的、新文件第一次访问是不是 MISS 第二次 HIT。这套流程跑通了才算真正接入完成。我见过太多“配置看起来都对一发版就出事”的案例问题全在流程没闭环。4. 缓存不刷新与回源风暴线上最常遇到的四类事故CDN 这东西配好之后平时像不存在一样出事的时候又特别难查因为你隔着一层看不见的中间层。下面四类是我遇到过频率最高的每一类都给出完整的排查链路照着走基本能定位。4.1 发版后用户看到旧页面缓存键和 TTL 的双重陷阱现象很典型代码已经发布了一部分用户还在用旧版本刷新也没用甚至换个浏览器就好了。排查顺序我一般是这样的。第一步确认 HTML 的缓存策略。用 curl 打首页看Cache-Control里有没有max-age大于 0看Age是多少。如果 HTML 被缓存了 10 分钟那发版后 10 分钟内所有用户看到的都是旧的这属于配置问题改规则、刷缓存即可。第二步确认服务端有没有注册 Service Worker。这是最阴的一个坑。SW 会拦截请求并优先返回本地缓存Cache-Control设成 0 都没用。查法很简单DevTools 的 Application 面板看有没有注册或者在无痕窗口打开对比。有 SW 的话要么发版时更新 SW 版本号要么用 skipWaiting 逻辑。第三步确认边缘缓存键有没有被版本参数污染。如果 HTML 的 URL 不带版本号但边缘节点缓存了它且 TTL 很长那唯一解法是主动刷新。这引出第四步。第四步主动刷新缓存。控制台一般提供按 URL 或按目录刷新还有按标签刷新。刷新的生效时间通常在几分钟内不是瞬时的因为要通知到所有节点。发版流程里应该固定一步上传新文件 → 刷新 HTML 和变更的路径 → 等待生效 → 验证。第五步确认用户的浏览器缓存。这一步经常被忽略。CDN 侧已经是最新了但用户浏览器里还存着旧 HTML。解法是给 HTML 设Cache-Control: no-cache或者max-age0, must-revalidate让它每次都向服务端发校验请求。我自己现在固定用这套组合HTML 不缓存 静态资源文件名带内容哈希 长 TTL immutable。这套组合下来发版就是“上传新文件 刷新一次 HTML”简单、可预测、不会出现半新半旧的错乱状态。4.2 预热没做回源把源站打挂事故现场晚上八点活动页上线运营在群里推了一波流量瞬间涌进来。CDN 命中率是 0因为没人访问过这个页面。所有请求同时回源源站 CPU 打满数据库连接池耗尽不但活动页打不开整个站点都挂了。这个链条的本质是未预热的冷启动 突发流量 全量回源。CDN 平时能帮你扛十倍流量是因为压力被缓存挡住了缓存是空的它就是一根导线把所有压力原样传导到源站。预防手段有几层最好是都做。预热。活动开始前主动把关键资源推到边缘节点。控制台一般支持批量提交 URL也可以自己写脚本遍历静态资源目录逐个请求。预热要在活动开始前足够早做因为分发到全部节点需要时间热门节点快冷门节点慢。源站限流与排队。在源站前面加一层限流比如 nginx 的limit_req把超过能力范围的请求挡掉或者排队宁可让部分用户慢也不让整个源站崩。这是兜底不是主要手段。回源收敛。确认 CDN 侧的请求合并是开启的。同一个资源在同一时刻的 N 个回源请求应该只放行 1 个。这个功能能极大削峰。stale-while-revalidate。这个响应头指令的意思是缓存过期后先返回旧内容给用户同时在后台异步回源更新。用户不会等待源站也只承受一次回源。配置得当的情况下热门内容几乎不会出现“过期瞬间集体回源”的尖峰。降级预案。关键页面准备一份纯静态的降级版本一旦源站不行了直接把加速域名切到静态兜底内容上。这个方案土但真出事的时候能保住基本可用。# 源站侧限流示例 limit_req_zone $binary_remote_addr zonecdn_zone:10m rate50r/s; location /api/ { limit_req zonecdn_zone burst100 nodelay; proxy_pass http://backend; }这里的rate50r/s是按单 IP 算的而 CDN 回源的源 IP 是节点 IP数量有限所以实际效果是按节点限流。如果你想让整体可控需要按节点 IP 段配置白名单并单独设阈值这个细节很多教程不会讲但不处理的话限流规则基本起不到保护作用。4.3 跨域与鉴权头被丢了CORS 预检失败排查现象本地开发一切正常接上 CDN 后接口全部报跨域错误控制台一片红。CORS 在 CDN 场景下容易出问题是因为中间多了一层请求头和响应头都可能被改写。先看预检请求OPTIONS有没有通过。浏览器发实际请求前会先发 OPTIONS如果 CDN 把 OPTIONS 当成普通请求缓存了或者直接返回了 405预检就失败。检查方法DevTools 的 Network 面板筛选 OPTIONS看状态码和响应头。再看Access-Control-Allow-Origin是不是被缓存串了。这个坑非常隐蔽。如果源站对不同的Origin返回不同的Access-Control-Allow-Origin值但响应里没有Vary: OriginCDN 会把第一次的响应缓存下来后面所有不同来源的请求都拿到同一个Allow-Origin值于是只有第一个来源能跨域成功其他全失败。解法是让源站返回Vary: Origin或者在 CDN 侧配置按 Origin 分流。第三看鉴权头有没有被丢。Authorization、自定义的X-Token这类头某些默认配置下不会透传到源站尤其是回源时重新构造请求的场景。检查方法是在源站日志里打印所有请求头比对 CDN 侧发出的和源站收到的。这个排查动作看着笨但最快。第四看 Cookie 的域和 SameSite。如果加速域名和主站域名不同Cookie 默认不跨域携带。需要设置Domain为父域同时SameSite不能是Strict。现象首先看什么常见根因全部跨域失败OPTIONS 请求状态码预检被缓存或返回 405只有部分来源失败响应头Vary缺少Vary: Origin缓存串了接口 401源站收到的请求头鉴权头未透传登录态丢失Cookie 的 Domain 与 SameSite跨子域未设置父域偶发失败是否命中了不同节点各节点缓存状态不一致处理完这些还要回归测试一遍不同来源、带凭证、不带凭证、GET 和 POST四种组合都跑一遍。跨域问题最烦的地方是“改好了 A 场景B 场景又挂了”一次性覆盖全比反复试错快。4.4 日志对不上命中率为什么和后台报表差一截控制台显示命中率 95%自己算日志却是 78%两边差得离谱。这种时候先别怀疑数据造假八成是口径不同。字节命中率和请求命中率是两回事。前者按流量算后者按请求数算。你的站点如果有一批超大文件视频、安装包命中率极高但海量小请求接口、埋点全部回源那字节命中率会很好看请求命中率很难看。看哪个取决于你的成本结构按流量计费就看字节按请求数计费就看请求数。206 分片请求会把统计打乱。视频拖动进度条会产生大量 206 请求每个都算一次请求但只传一小段数据。如果统计口径没排除命中率会被拉低。日志采样。高流量站点为了控制成本日志可能是采样的比如每 100 个请求记一条。你拿采样日志去算全量命中率误差自然大。边缘到客户端中断的请求也会被记录。用户切后台、关页面导致连接中断这类请求在服务端看来是失败的但不算回源统计口径如果没排除也会影响结果。不同节点的状态不一样。新上线的节点、刚被刷新的路径、长时间没访问的长尾资源命中率天然低。全局平均值掩盖了局部分布要按地区、按状态码拆开看。我一般的做法是不看总的命中率而是按“文件类型 状态码 地区”三个维度拆开找出拉低平均值的那一小撮。通常是某一类接口全量回源或者某地区的节点缓存异常。这种拆法比盯着一个总数有用得多。5. 成本与效果怎么算CDN 账单里几个容易被忽略的计费维度最后聊钱。CDN 的账单结构比想象中复杂很多人第一次看到账单都懵明明流量没超费用怎么翻倍了。5.1 流量、请求数、回源流量三种计费口径计费项计量对象典型触发场景能省的地方边缘流出流量节点发给用户的字节数所有正常访问提高命中率、压缩、图片转 WebP/AVIF请求数节点收到的 HTTP 请求次数小文件多、接口多、埋点密集合并请求、减少不必要的轮询回源流量节点从源站拉取的字节数未命中、缓存过期、刷新后首次访问提高 TTL、预热、分层回源动态请求数走动态加速的请求接口调用能缓存的下沉到静态刷新/预热次数提交的 URL 数量频繁发版、大目录刷新用标签刷新替代目录刷新几个容易被忽略的点。回源流量通常单独计费而且单价可能高于边缘流量。这意味着“命中率低”不只是性能问题是直接的钱的问题。100GB 边缘流量和 100GB 回源流量价格可能差好几倍看具体服务商。请求数在小文件场景下会成为主要成本。一个页面 200 个请求日活 10 万一天就是 2000 万请求。如果每个请求都是小图、图标、埋点流量没多少请求费用很可观。合并雪碧图、用图标字体、减少埋点上报频率这些前端优化在这里直接体现为账单下降。HTTPS 请求可能单独计价。有些服务商的报价里HTTPS 请求数比 HTTP 贵。全站 HTTPS 是必然的但要知道这部分成本存在。刷新缓存本身可能不收费但“刷新的后果”收费。刷新后第一次访问必然回源如果频繁刷新大目录等于反复制造回源流量。5.2 命中率提升 1 个百分点账单会变多少算个具体的例子让数字说话。假设日边缘流量 10TB当前命中率 92%那么回源流量是 10TB × (1 - 92%) ≈ 0.8TB。如果回源流量单价是边缘流量的 3 倍那么回源部分的成本相当于 2.4TB 的边缘流量总等效成本 11.6TB。把命中率提到 95%回源变成 0.5TB等效成本 1.5TB总等效成本 11.0TB。也就是命中率提升 3 个百分点成本降了约 5%。看起来不多但这是纯利润而且流量越大基数越大。再看另一个方向。如果通过压缩和图片格式转换把边缘流量本身从 10TB 压到 7TB命中率不变那成本直接降 30%。这说明减少总字节数比提高命中率的杠杆更大但前者需要改前端代码后者只要调 TTL见效快慢不同。实际优化顺序我一般建议先开压缩和 WebP一天能做完再调缓存规则提高命中率一周内观察数据最后才考虑架构级的分层回源成本高、周期长。还有带宽峰值这个维度。如果源站是按固定带宽计费的假设峰值 200Mbps月费按带宽单价算。接入 CDN 后源站峰值可能降到 20Mbps省下的带宽费可能比 CDN 流量费还多。这个账要一起算不能只盯着 CDN 账单。5.3 什么情况下自建边缘比买 CDN 更划算不是所有场景都适合买 CDN。以下几种情况自建或者在核心机房多放几台缓存服务器可能更划算。用户高度集中在一个区域。比如你的用户 90% 在同一个城市那在这个城市建设一个缓存节点或者干脆买台大带宽机器成本可能比 CDN 低时延也不差。CDN 的优势是“广覆盖”用户不广优势就发挥不出来。流量极大且平稳。日均几百 TB 且波动小这种规模下自建的成本曲线会低于按量计费。但要注意自建还需要考虑运维人力、故障处理、证书管理这些隐性成本只算带宽费会低估。内容高度私密不适合放第三方节点。这类场景通常会选择自建或者私有化部署的边缘缓存。反过来以下几种情况基本不用犹豫直接买用户全球或全国分散、流量波动大有秒杀、有活动、团队里没人专职做网络运维、需要快速上线。CDN 最大的价值是把“网络工程”这件事外包出去你按量付费省掉的是人的成本和试错成本。还有一个混合思路值得一试核心静态资源走商业 CDN超大的安装包、视频源文件走对象存储直链或者自建下载节点。因为大文件的流量成本占比高而 CDN 对大文件的单价优势不明显。这个组合在一些下载站上很常见。6. 几个我踩过之后才记住的细节写到这里说几个不成体系但确实省过我很多时间的点。接入前先量基线。用真实网络环境不是本地 localhost测一遍首屏时间、TTFB、各资源耗时存一份报告。接入后再测一次对比才有意义。没有基线的优化全是玄学你甚至无法判断是不是变慢了。给所有静态资源加内容哈希然后就可以忘掉它们。这是我最推荐的一个改造。文件名带哈希之后缓存策略变得极其简单静态资源永久缓存HTML 不缓存。不需要复杂的刷新流程不需要担心用户看到旧版本发版就是上传文件。把验证命令写进发版脚本。发版后自动 curl 几个关键 URL检查X-Cache和内容哈希是否匹配不匹配就告警。这一步能抓住绝大部分“配置漂移”导致的问题。人手动验证一定会偷懒脚本不会。控制台的图表有延迟别当成实时监控。出故障的时候盯着控制台的曲线看往往会误判。直接 curl 打请求看响应头比任何图表都准。故意制造一次小故障来验证兜底。把源站停掉看 CDN 是返回错误页还是返回旧缓存。这个演练做一次你就知道自己配置的 stale-if-error 到底有没有生效。我第一次做这个测试的时候发现配置里写了但实际上没起作用原因是响应头被源站覆盖了。不同地区的调度一定要实测。找几个异地朋友或者用多点探测工具跑一遍解析和访问。调度错了后面所有优化都是白费。别在业务高峰期改缓存规则。规则一变命中率立刻掉回源压力瞬间上涨。要改就挑低峰期改完盯着回源率和源站负载看半小时。保留一份“紧急全站刷新”的操作手册。出事的时候人是慌的按着手册一步步点比临场回忆控制台在哪个菜单里靠谱得多。手册里写清楚刷新入口在哪、刷新后多久生效、怎么验证、找谁 escalate。我自己在这个领域折腾几年下来最大的体会是CDN 从来不是“配完就不用管”的东西它更像一个需要长期观察的缓存系统你对内容的变化规律理解得越清楚配置就越简单账单也越好看。真正麻烦的从来不是原理是那些“看起来应该没问题”的地方——回源 Host 少写了一个字符、Vary头漏了一个、TTL 单位看错了一位。这些坑没有捷径只能靠每次上线都老老实实验证一遍。
企业数字化 ERP 产品动态
相关推荐
回溯与动态规划解决组合求和问题 1. 问题背景与需求拆解 小明减肥这道算法题源自华为OD机考双机位A卷,题目描述大致为:小明需要在N天内通过运动减肥,每天有若干种运动方式可选,每种运动消耗不同的卡路里。要求找出所有满足总消耗卡路里等于目标值的运动组合。这本… · 2026/11/3 12:25:45
Paperclip协议:轻量级AI Agent协同开发范式 1. “Paperclip”不是回形针:它是一套AI Agent协同开发范式 你搜“paperclip”,第一反应是办公桌抽屉里那枚银色小金属?别急——在2024年中后期的工程圈,尤其是Node.js与React深度使用者的私聊频道、GitHub Star飙升仓库、以及Ope… · 2026/11/3 12:25:45
EPSON机器人现场调试指令字典:系统级命令执行逻辑与避坑指南 简介:本资源是EPSON爱普生机器人官方指令集的完整中文整理版,面向工业机器人工程师、自动化系统集成人员及高校机电/自动化专业学习者,解决EPSON RC控制器编程中指令查询不便、功能理解不深、调试效率低等实际问题。PDF文档共1份,… · 2026/11/3 12:25:45
Kubernetes在AI运维中的实践与优化 1. 项目概述:当AI遇上Kubernetes的运维革命 在云计算和AI技术双轮驱动的今天,传统运维模式正面临前所未有的挑战。我最近在腾讯云TKE上部署AI工作负载时,深刻体会到Kubernetes(简称K8S)作为容器编排的事实标准… · 2026/11/3 14:26:25
Model-Optimizer:面向生产落地的模型瘦身工程方法论 1. 这不是“一键压缩”工具,而是一套模型瘦身的手术方案 “Model-Optimizer”这个词最近在工程师茶水间、技术群和内部分享会上出现频率陡增,但它绝不是某个新出的GUI软件图标,也不是点一下就变快的魔法按钮。它代表的是一整套面向生产落地的… · 2026/11/3 14:26:25
BT.656视频接口解析:SAV/EAV同步与裸流解帧实战 简介:这份文档围绕ITU-R BT.656数字视频接口标准展开,面向从事视频采集、FPGA/SoC接口开发与嵌入式图像处理的初中级读者,帮助理清BT.601与BT.656的隶属关系和工程差异。内容重点讲解8/10位数据传输、27MHz并口或243Mb/s串行接口、先Y后UV的传… · 2026/11/3 14:26:25
VCM音圈马达:从原理到选型调试的完整指南 手机摄像头能自动对焦,背后那个比指甲盖还小的部件,十有八九就是VCM音圈马达。很多人第一次听到这个词,会以为是什么高深的通信协议或者音频处理芯片,其实它干的事情特别朴素——推着镜头前后移动,让画面变清楚。我接触… · 2026/11/3 14:26:25
大模型数据预处理实战:从数据源到动态Padding的流水线方案 「昇思 MindSpore 大模型」这句话里,真正决定一次微调能不能顺利跑通的环节,往往不是模型结构,而是那串看起来平平无奇的 mindspore.dataset 。我在好几个大模型项目里见过同一种症状:训练脚本没问题,模型结构没问题… · 2026/11/3 14:25:24
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46