CDN 场景下的排障,比单机服务更容易让人迷失。因为用户请求并不是直接到源站,而是先到边缘节点,再根据缓存、调度和策略决定是否回源。链路一旦变长,问题也更容易被中间层掩盖。

CDN 排障真正的难点,不是概念多,而是参与者变多了。客户端、DNS、边缘节点、回源链路、源站本身,任何一层异常都可能在用户视角里表现成同一种“访问异常”。

先把整条链路画出来

排查 CDN 相关问题时,脑子里最好先有一张最小路径图。

用户发起请求,先完成域名解析,再被调度到某个边缘节点。边缘节点会先判断本地是否命中缓存,如果命中,直接返回;如果未命中,或者缓存过期,就继续向源站回源获取内容,然后再把结果返回给用户。

所以同一个“访问慢”现象,可能分别来自下面几类原因。

  • 调度到了不合适的边缘节点。
  • 边缘节点没有命中缓存。
  • 回源链路慢。
  • 源站本身处理慢。
  • 中间代理或安全策略影响了请求。

一组足够实用的排查手段

1
2
3
4
curl -I -v https://example.com/resource
curl -o /dev/null -s -w 'dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} starttransfer:%{time_starttransfer} total:%{time_total}\n' https://example.com/resource
dig example.com
traceroute example.com

在 CDN 场景里,curl -I -v 特别有价值,因为很多平台会在响应头里带出缓存状态、边缘节点信息、回源痕迹或链路标识。

虽然不同 CDN 厂商的头部名字不一样,但观察思路是一样的:先看有没有命中缓存,再看响应路径有没有表现出回源特征。

一个比较稳的排查顺序

第一步,先确认调度结果。

1
dig example.com

这里先看域名最终解析到哪里。如果不同地区、不同 DNS 返回的边缘地址差异很大,就要考虑调度策略本身是否合理。

第二步,看请求阶段耗时。

1
curl -o /dev/null -s -w 'dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} starttransfer:%{time_starttransfer} total:%{time_total}\n' https://example.com/resource

如果前面的 DNS、建连和 TLS 都不高,但首字节时间明显偏大,就很值得怀疑边缘未命中缓存或回源慢。

第三步,看响应头。

1
curl -I -v https://example.com/resource

此时重点不是只看状态码,而是看有没有缓存命中标识、边缘节点标识、源站相关头部或者明显的代理痕迹。

第四步,如果你能在边缘或源站侧抓到包,就继续确认回源过程有没有异常,比如源站连接慢、TLS 握手慢、回包慢、上游重传等。

怎么区分是边缘慢还是源站慢

一个很常用的思路,是先比较缓存命中和未命中的表现差异。

如果同一个资源第一次慢、第二次明显快,通常就说明第一次可能经历了回源,第二次命中了边缘缓存。

如果每次都慢,而且首字节一直很高,那更要继续区分是边缘节点处理压力、回源链路问题,还是源站响应慢。

如果命中缓存时依然很慢,就不能再只盯着源站,而要看边缘节点、调度节点或客户端到边缘之间的路径质量。

这也是 CDN 排障里非常重要的一点:不要因为“用了 CDN”就默认所有问题都在源站,也不要因为“回源了”就忽略客户端到边缘这一段链路。

常见误判

第一个常见误判,是把用户看到的 5xx 全部当成源站问题。实际上,边缘节点、回源超时、上游握手失败、代理规则异常,都可能最终表现成 5xx。

第二个常见误判,是只看状态码不看链路。状态码只告诉你结果,不告诉你请求是在哪一层、哪个节点上变坏的。

第三个常见误判,是忽略缓存状态。很多时候用户抱怨“偶尔慢”,根本原因就是命中和未命中混在一起,导致体验不稳定。

一个值得自己做的小实验

选一个带静态资源的站点,连续请求同一个文件几次。

1
2
3
curl -I -v https://example.com/static/app.js
curl -o /dev/null -s -w 'dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} starttransfer:%{time_starttransfer} total:%{time_total}\n' https://example.com/static/app.js
curl -o /dev/null -s -w 'dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} starttransfer:%{time_starttransfer} total:%{time_total}\n' https://example.com/static/app.js

重点观察。

  • 两次请求的首字节时间是否明显不同。
  • 响应头里是否出现缓存状态变化。
  • 如果换网络环境或换 DNS,调度结果是否变化。

这个实验能非常直观地帮助你理解什么叫“边缘命中”和“回源开销”。

小结

CDN 排障之所以难,不是因为它神秘,而是因为它把一条本来就不短的网络链路又拉长了一层。只要你养成从调度、边缘、缓存、回源、源站这几个环节逐层拆开的习惯,再配合 digcurl 和必要的抓包去验证,很多看似复杂的 CDN 问题其实都会有清晰的落点。排障最怕一团糊,而 CDN 场景最需要的,恰恰就是把这团糊重新拆成一段一段可验证的链路。