CDN预热失败时,运维通常先看CDN控制台的重试记录,但如果不回到源站侧做对比验证,很容易把问题停留在表面。预热请求本质上是CDN边缘节点代替用户提前回源,节点会按照配置并发拉取指定URL。源站对这批请求的处理方式,直接决定任务能否成功。常见的失败信息中,一部分是等待源站响应超时,另一部分是连接被拒绝或重置。这两种信号分别对应源站响应速度慢和连接数达到上限,排查路径完全不同。

一、源站响应慢如何导致预热失败
CDN节点在预热时通常有一个回源超时阈值,不同厂商默认值可能在5秒到30秒之间。如果源站不能在阈值内返回完整响应,节点会中断本次拉取并记录失败。需要注意的是,这里说的响应慢不一定指整站变慢,可能只是预热目标文件的生成路径慢。比如很多站点把HTML页面静态化,但JS或CSS文件由后端应用实时合并输出,首次预热时应用需要加载配置、查询数据库、写临时文件,耗时很容易超过CDN节点的容忍值。
判断源站响应速度,可以从源站本地和外部两个视角分别测试。在源站本机执行curl命令,能排除公网链路波动;从CDN节点或外部服务器测试,则更接近预热时的真实网络路径。下面是一个常用的耗时分解命令:
curl -o /dev/null -s -w 'time_namelookup: %{time_namelookup}\ntime_connect: %{time_connect}\ntime_starttransfer: %{time_starttransfer}\ntime_total: %{time_total}\nhttp_code: %{http_code}\n' https://ipipp.com/static/app.js
输出中如果time_starttransfer明显偏大,说明源站首字节响应慢,可能在后端处理或磁盘读取;如果time_total与time_starttransfer差值很大,说明传输过程带宽不足或文件体积过大。预热任务通常拉取的是静态文件,源站应当保证这些文件走本地缓存或对象存储,而不是每次请求都经过复杂逻辑。对动态生成的资源,可以提前生成到磁盘,或者用短期缓存头告诉CDN节点允许复用。
二、连接数限制触发预热的典型表现
预热任务往往并发请求多个URL,如果源站配置的连接上限较低,会出现大量SYN包被丢弃或accept队列溢出。此时CDN侧看到的错误通常是连接超时、连接被拒绝,甚至源站直接返回502或503。与响应慢不同,连接数限制的失败往往带有突发性:预热一开始失败率不高,随着并发增加错误率迅速上升,预热结束后源站访问又恢复正常。
排查连接数问题需要同时关注操作系统、Web服务器和应用程序三层。操作系统层面可以用ulimit -n查看单个进程可打开的文件描述符上限,如果Nginx进程达到这个值,日志里会出现Too many open files。Nginx本身的worker_connections决定了每个worker能同时处理的连接数,乘上worker进程数才是总连接能力。应用程序如果是PHP-FPM、Java或Node服务,也可能因为进程数不足而无法及时处理新连接。
ss -s
netstat -an | awk '/^tcp/ {print $6}' | sort | uniq -c | sort -rn
执行上述命令后,如果SYN-RECV数量异常升高,说明握手完成前就出现积压;如果ESTABLISHED总数已接近Nginx计算出的理论并发值,那就需要调大连接参数或增加源站实例。还要检查云安全组或防火墙是否设置了并发会话限制,这类限制常常被忽略,但预热流量会集中触发。
三、优化源站响应与连接复用策略
解决响应慢,优先考虑让预热目标文件真正静态化。将JS、CSS、图片等资源直接存放到对象存储或CDN源站目录中,避免经过动态路由。对于必须动态生成的资源,可以设置较短的内部缓存时间,例如Nginx的proxy_cache或应用层缓存,让第二次回源时直接命中。源站还可以开启gzip或brotli压缩,降低传输体积,但这会增加CPU消耗,需要根据实际文件类型权衡。
连接复用是降低预热对源站冲击的有效手段。HTTP/1.1默认支持Keep-Alive,但如果Nginx反向代理到后端时没有正确传递Connection头,长连接会被降级为短连接,每个请求都要重新握手。配置Nginx的upstream块时,可以显式指定keepalive连接池,并设置合理的代理超时时间。下面是一个常见配置:
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 16384;
use epoll;
}
http {
upstream origin_pool {
server 192.168.1.10:80;
keepalive 64;
}
server {
listen 80;
location /static/ {
proxy_pass http://origin_pool;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_connect_timeout 3s;
proxy_read_timeout 10s;
proxy_send_timeout 10s;
}
}
}
这段配置中worker_rlimit_nofile提升进程可打开文件数,worker_connections扩大每个worker的连接上限,upstream里的keepalive 64保持后端长连接复用,减少CDN回源时的握手开销。需要注意的是,keepalive值不是越大越好,过大会占用源站连接池,反而不利于其他正常流量,可以根据单次预热的并发数和源站内存情况调整。
四、预热策略调整与降级方案
即使源站调优完成,也不建议一次性把几万个URL全部推给CDN做预热,尤其是源站带宽和连接数本就紧张的业务。可以先按文件热度排序,优先预热核心页面依赖的JS和CSS,再预热图片和字体。执行时采用分批提交,每批控制在50到200个URL,批与批之间留出几秒间隔,让源站有时间释放连接。多数CDN控制台支持分批次创建预热任务,也可以用脚本配合API实现。
#!/bin/bash
# 将待预热URL列表按每批50个拆分,避免源站瞬时连接数过高
split -l 50 urls.txt batch_
for f in batch_*; do
while read -r url; do
curl -s -o /dev/null -X GET "$url" &
done < "$f"
wait
sleep 2
done
对于源站连接数容易被打满的站点,还可以把预热流量引导到独立的源站域名或只读副本,避免影响线上写入请求。例如将静态资源切到对象存储后,预热任务直接回源到对象存储,连接能力通常远高于自建Nginx。若无法立刻迁移,可在预热时段临时调高安全组连接数阈值或增加源站实例,预热完成后再恢复,这样能明显降低失败率。最后建议在监控中把CDN预热失败数和源站TCP连接数放在同一个看板里,出现失败时先看连接数和响应耗时,再决定是扩容还是优化资源。

