X-Magento-Cache-Debug: HIT 很容易让人得出一个过早的结论:既然没有运行 Magento PHP,页面就应该瞬间返回。实际上,HIT 只回答“Varnish 是否找到了对象”,没有回答对象在什么时候可用、请求在它前后等待了多久,也没有说明浏览器完成首屏需要下载多少资源。

先把“慢”拆成三个时间

用 curl 同时记录 DNS、连接、TLS、首字节和总时间,避免只看浏览器瀑布图的一个色块:

curl -sS -o /dev/null   -w 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} start=%{time_starttransfer} total=%{time_total}
'   https://www.example.com/category.html

time_starttransfer 很高而 connect、TLS 很低,等待发生在 CDN、负载均衡或 Varnish 一侧。若首字节很快、total 很高,应查响应体大小、压缩和网络吞吐。HTML 很快而页面可交互很慢,则要转向 JS bundle、字体、图片和第三方脚本,继续调 Varnish 没有意义。

HIT 也可能包含等待

查看响应中的 AgeViaX-Varnish 和自定义 cache debug 头,同一个 URL 连续请求五到十次。只有部分请求慢,常见原因是连接池耗尽、线程排队、磁盘存储延迟,或 CDN 到源站偶发重连。全部请求稳定偏慢,则检查 VCL 中是否执行了昂贵的正则、DNS 查询、日志管道阻塞或外部 VMOD。

for i in $(seq 1 8); do
  curl -sSI https://www.example.com/category.html     | egrep -i 'HTTP/|age:|x-varnish:|x-magento-cache-debug:'
done

还要注意请求合并。一个对象过期时,首个请求去后端刷新,其余请求可能等待 busy object;最终响应依然可能表现为命中或使用 grace,但这段等待真实存在。结合 varnishstat 观察 MAIN.sess_queuedMAIN.threads_limitedMAIN.cache_hitMAIN.cache_miss,不要只截取命中率。

确认是否在用过期对象和 grace

Varnish 的 grace 能在后端异常或刷新期间继续返回旧内容,是稳定性工具,不是免费性能。若健康探测把后端标记为 sick,站点可能长时间依赖 grace;若对象频繁过期,刷新请求会持续打到 PHP。检查后端健康状态、对象 TTL 以及 Magento 发出的缓存标签清理频率。

varnishadm backend.list
varnishstat -1 | egrep 'cache_hit|cache_miss|backend_fail|threads_limited|sess_queued'
varnishlog -g request -q 'ReqHeader:host eq "www.example.com"'

日志量很大时应限制时间和 URL,生产环境不要长时间运行无过滤的 varnishlog

ESI、私有内容和 AJAX 是另外三条请求

Magento 的标准 private content 主要通过浏览器端 customer-data 更新,但站点可能自己增加 ESI 或 AJAX 区块。HTML 命中不代表这些子请求命中。迷你购物车、推荐、库存、地区价格等接口只要有一个串行等待,用户就会感觉页面慢。

在 Network 中按 Initiator 排序,看首个 HTML 后谁触发了 XHR;对慢接口单独用 curl 测量。若接口依赖客户会话,不能简单纳入公共缓存。应减少同步依赖、设置合理超时,或者把非首屏信息延迟加载。

响应压缩与静态资源不能忽略

同一份缓存 HTML 可能在 CDN 与浏览器之间没有 Brotli/gzip,或者 Vary: Accept-Encoding 处理不当导致对象变体混乱。检查实际传输大小,而不是模板源文件大小。静态资源方面,数百个小文件、巨型 source map、未优化图片和阻塞字体都会让“缓存页”显得很慢。

curl -sS --compressed -D - -o /dev/null https://www.example.com/category.html
curl -sS -H 'Accept-Encoding: identity' -D - -o /dev/null https://www.example.com/category.html

比较两次的 Content-EncodingContent-LengthVary。如果 CDN 会重新压缩,分别从公网域名和绕过 CDN 的受控源站地址测试,但不要在公开环境泄露源站入口。

一条可执行的判断顺序

先用 Timing 明确慢在连接、首字节还是下载;再确认慢请求究竟是 HTML、接口还是静态资源;只有 HTML 首字节慢时才深入 Varnish 队列、busy object、grace 与后端健康。最后用相同 URL、相同设备、相同地区重复测量 P50 与 P95。单次 HIT 截图只能证明那一次有缓存头,不能证明缓存链路没有排队。