EdgeOne 全球加速实战:美国访问从 1.2 秒到 0.5 秒

AI 摘要997笔记AI

站点迁至腾讯云 EdgeOne 后,国内访问为 60-100 毫秒,美国首字节却达 1.2 秒。排查发现,带时间戳参数会误判缓存,且加速区域未真正全球、HTML 因 no-cache 从不缓存。改为全球加速并开启节点缓存,用规则排除后台和动态脚本后,美国首字节降至约 0.5 秒、图片 225 毫秒,国内基本不变。

此内容由AI根据文章内容自动生成。

站点搬到腾讯云 EdgeOne 之后,国内访问一直是 60-100 毫秒,体验很好。直到有一天在美国的机器上随手 curl 了一下首页,发现首字节要 1.2 秒——比国内慢了十几倍。

这篇记录完整的排查和优化过程:怎么确认瓶颈、怎么把「全球加速」真正打开、以及一个让很多人白忙半天的测量陷阱。最后美国访问从 1.2 秒降到 0.5 秒,全站图片 225 毫秒。

这篇适合谁

已经在用 CDN 或 EdgeOne、但海外访问仍然慢的站长。全文不需要改一行代码,全部在控制台和几个 API 调用里完成。

一、先看清「慢」在哪一层

很多人把「用了 CDN」当成「已经加速了」。实际上现代站点至少有三层缓存,各自负责不同的事:

三层缓存结构:EdgeOne 边缘缓存、服务器页面缓存、数据库
边缘缓存存 HTML 和静态资源,源站缓存存渲染结果,数据库存原始数据

边缘缓存(CDN)解决的是「用户到最近的节点要多久」;源站缓存(FastCGI、页面缓存)解决的是「渲染一个页面要多久」;数据库则是最后一层。三层里任何一层缺失,用户都会感受到。

我这里的情况是:源站已经是 FastCGI 缓存命中(X-Cache: HIT),国内 67 毫秒;但边缘那层对 HTML 是完全不缓存的,所以美国用户的每一个请求都要跨太平洋回源一次。

二、测量陷阱:别加时间戳参数

排查初期我犯了个错,值得单独拎出来说。

测性能时习惯性加了防缓存参数:

curl -I "https://www.997wan.com/?t=1699999999"

结果永远是 X-Cache: BYPASS、EO-Cache-Status: MISS,于是我判断「缓存没开」——其实开着的。

原因在源站的缓存规则里:

# 宝塔 fastcgi_cache 的典型写法
if ($query_string != "") {
    set $skip_cache 1;
}

只要 URL 里带任何查询参数,页面缓存直接跳过;边缘节点看到源站返回的 no-cache,也跟着不缓存。加了参数的测量只代表「冷启动」,不能代表真实用户体验。

正确的测法是原样请求,多跑几次看命中率:

for i in $(seq 1 12); do
  curl -s -o /dev/null -w "%{time_starttransfer} " \
    -D /tmp/h.txt https://www.997wan.com/
  grep -i eo-cache-status /tmp/h.txt
done

三、把加速区域改成全球

EdgeOne 的加速区域默认可能是「中国大陆」或「亚太」,这时候海外用户会被调度到最近的境外节点——通常只有新加坡一个出口。

实测方法很简单,从不同地区的解析器看返回的 IP:

# 国内视角
dig +short www.example.com

# 海外视角(用公共 DoH)
curl -s -H "accept: application/dns-json" \
  "https://cloudflare-dns.com/dns-query?name=www.example.com&type=A"

如果两边返回的 IP 归属地一致(比如都指向新加坡),说明还没有真正的多区域覆盖。在控制台把加速区域改成「全球」后,欧洲、北美会各自有节点。

注意这里有个容易误判的点:EdgeOne 用 Anycast,同一个 IP 在全球不同位置会落到不同的物理机房。所以不能只看 IP 归属地,要看各地实测延迟。

四、真正的瓶颈:HTML 从不缓存

把区域改成全球之后,美国访问从 1.2 秒降到 0.9 秒左右,但还不够。继续查发现根因在站点级配置:

# 查询站点缓存配置(tencentcloud teo API)
# CacheConfig.Cache.Switch        = off      ← 节点缓存总开关:关
# CacheConfig.FollowOrigin.Switch = on       ← 跟随源站:开

而 WordPress 对 HTML 返回的响应头是:

Cache-Control: no-store, no-cache, must-revalidate

EdgeOne 非常守规矩地遵守了它——不缓存。图片之所以是 HIT 的,是因为源站给静态资源设了 max-age=2592000,源站允许缓存。

换句话说:规则里写什么 TTL 都没用,因为总开关是关的,而「跟随源站」又让 no-cache 生效了。

五、开节点缓存 + 加三条规则

正确的做法是两件事一起做:

第一,站点级把节点缓存打开,并关掉「跟随源站」。TTL 建议 300 秒,不要一上来就设几小时,否则后台改完文章前台要等很久。

第二,用规则引擎把不该缓存的路径排除掉。这一步必须做,否则后台会被缓存,你进 wp-admin 会看到五分钟前的旧页面。

规则引擎的四层优先级:先排除后台,再设置页面缓存
规则按优先级从高到低匹配,越靠前越先生效

我的四条规则,优先级从 1 到 4:

# 优先级 1:动态脚本一律不缓存
lower(${http.request.file_extension}) in ['php','jsp','asp','aspx']  → 不缓存

# 优先级 2:后台与管理接口不缓存
${http.request.uri.path} matches '^/(wp-admin|wp-json|wp-login|wp-cron|xmlrpc)'  → 不缓存

# 优先级 3:只对首页和文章页给 300 秒
${http.request.uri.path} matches '^/([A-Za-z0-9_-]+/)?$'  → 节点缓存 300 秒

# 优先级 4:全站兜底 300 秒,图片单独 3600 秒
${http.request.host} in ['*.example.com']  → 300 秒 + 图片 3600 秒

有个细节要注意:条件表达式里的正则必须用单引号。写成双引号会被判定为语法错误,报错信息只说「syntax error at offset N」,不告诉你是引号问题。

六、实测:中美两地对比

优化前后中美两地首字节时间对比
同一张图、同一个页面,优化前后各测十二次取中位数

用美国洛杉矶的一台 VPS 做实测(不要用国内机器测海外,链路完全不同):

# 美国机器上执行
for i in $(seq 1 12); do
  curl -s -o /dev/null -w "%{time_starttransfer}\n" \
    -D /tmp/h.txt https://www.example.com/
  grep -i eo-cache-status /tmp/h.txt
done

结果:

项目 优化前 优化后
美国 · 首页首字节 1.15 – 1.35 秒 0.49 – 0.57 秒
美国 · 缓存命中率 0%(全 MISS) 100%(12/12 HIT)
美国 · 图片 1.2 秒(回源) 0.22 秒
中国 · 首页首字节 67 – 85 毫秒 101 毫秒(基本不变)
后台 wp-login 不缓存 不缓存(200 正常)

美国从 1.2 秒降到 0.5 秒,提升约 2.4 倍。国内基本没变化——因为源站就在隔壁,边缘缓存帮不上太多忙。

七、踩过的坑

1)用 API 改规则比控制台麻烦得多。腾讯云 TEO 的 ModifyL7AccRule 要求传一个和查询结果结构不同的对象,就算把查出来的规则原样回写也会报 InvalidParam。正确姿势是「删掉旧的 + 新建一条」,而新建时不能带优先级字段,系统会自动分配。

2)规则改动不是实时的。改完到全节点生效要几分钟,而且每个边缘节点各自独立缓存——新节点第一次请求必然是 MISS,别看到一两次 MISS 就以为没生效。

3)缓存键里的查询字符串不要关。如果关掉,/?s=关键词 这类搜索页会和首页共用同一个缓存键,用户搜索会拿到首页内容。默认是包含查询字符串的,别动它。

4)后台排除规则优先级一定要高于缓存规则。规则是按优先级顺序匹配的,如果把「全站缓存」排在前面,wp-admin 会被一起缓存。

八、这套方案的边界

做完之后美国首都圈的延迟稳定在 0.5 秒左右,但和「美国本地站点」的 30 毫秒还是有差距。拆开看:连接 72 毫秒、TLS 149 毫秒、首字节 490 毫秒——TLS 握手完成之后还要 340 毫秒才出首字节,说明服务这个请求的节点离洛杉矶大约 170 毫秒,可能在美东或欧洲。

这是 CDN 厂商的节点覆盖决定的,调参解决不了。想再快,只有两个方向:一是等厂商在当地加节点,二是把静态资源进一步压缩,用更小的体积抵消距离。

最后的建议:先把测量方法搞对。我前面因为加了 ?t= 参数,得出过「缓存完全没开」的错误结论,白折腾了半天。看到 BYPASS 和 MISS 的时候,先确认请求本身是不是干净的。

上一篇 自建 DoH 服务器保护隐私:mosdns + unbound 从零到上线