
很久以前写了一个关于空气污染数据的接口,后端为 aspnetcore 3.1,前端随便糊了几个图表,通过 openresty 承载静态前端网页,代理并缓存来自后端的数据
nginxupstream upstream1 {
server service1:5000;
keepalive 3;
}
server {
listen 80;
server_name api.example.com;
root /www/chart;
index index.html;
gzip off;
access_log off;
error_log off;
real_ip_header CF-Connecting-IP;
proxy_cache zone1;
add_header X-Openresty-Cache-Status $upstream_cache_status;
location /data/mainland.json {
proxy_http_version 1.1;
proxy_pass http://upstream1/Data/MainLand;
proxy_set_header X-Real-IP $http_cf_connecting_ip;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Connection '';
proxy_ignore_headers Cache-Control;
expires 1h;
}
location /data/hongkong.json {
proxy_http_version 1.1;
proxy_pass http://upstream1/Data/HongKong;
proxy_set_header X-Real-IP $http_cf_connecting_ip;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Connection '';
proxy_ignore_headers Cache-Control;
expires 1h;
}
location ~* .(js|css|jpg|gif|png)$ {
expires 7d;
}
}
以前是通过和某某云的员工批歪交易了一下,弄了个云分发来抗,源机压力不大。最近该员工即将离职了,我也早做打算开始搬家,准备用 CloudFlare 来抗,反正也不用考虑国内流量。开了个免费套餐,按经验配置完之后就睡觉去了,结果一觉起来源机早就挂了,源机配置比较差,QN 机房的物理机,x5670 双路,24G 内存,100M 带宽,重启机器后看日志发现接入 CloudFlare 不到 1 小时就被压死了。
恢复服务后,监控流量发现 QPS 已经高达 5000,即每秒 5000 个请求,带宽都直接拉满了,正常流量对于源机来说已经算是 DDOS 了。
经过检查,发现问题主要出在 /data/*.json 这些接口地址上,套 CloudFlare 后仅有前端文件(.css、.js)同时满足 X-Openresty-Cache-Status: HIT 和 cf-cache-status: HIT,被转发的接口并则是 X-Openresty-Cache-Status: HIT 和 cf-cache-status: DYNAMIC,即本机 openresty 正确将接口数据缓存了,但 CloudFlare 那边直接回源了(DYNAMIC 代表部分回源,但绝大多数情况下是完全回源)。
本以为免费套餐的限制,于是花了 100 元买 20 刀开通了专业版套餐,发现现象依旧。联系客服后得知,原来 CloudFlare 默认是按文件后缀名而不是 MIME 配置缓存的,样式表和脚本自然可以被缓存,但这个默认规则将 *.json 视为动态请求排除在外。
csharp// 伪代码
switch(url.postfix){
case 'txt':
case 'js':
case 'css':
case 'ttf':
case '......':
allpyCache(context);
break;
default:
transport(context);
break;
}
按客服的提示,将 缓存 -> 配置 -> 缓存级别 设置为忽略查询字符串,然后去 规则 -> 页面规则 中新建一条规则
ini[匹配]
api.example.com/data/*
[缓存级别]
缓存所有内容
[源服务器缓存控制]
启用
[边缘缓存 TTL]
与 浏览器缓存 TTL 相同
[浏览器缓存 TTL]
与 边缘缓存 TTL 相同

设置完成后不到 1 分钟就看到源机压力立刻下降了,5 分钟不到就几乎归零了。
不太清楚为什么 CloudFlare 要通过文件后缀名来设置缓存,一般都是用 MIME 配置的,很多文件存储(图床、视频等)网页应用都是没有后缀名的,比如 https://static.example.com/file-content-hash/format/webp/width/320 这样的形式,这种站默认情况下就完全穿透了。