太长不看,以后 再出该问题 跳到 十二 , 若做了 snapshot 恢复 跳到 十三
=========================================================
【服务器 PHP 5.3 老站高负载 / 爬虫拥堵处理及恢复记录】
环境:
Debian + MW-Panel(mdserver-web)
OpenResty / Nginx
PHP 5.3
MySQL 5.7
服务器:1 核 / 2GB 内存
网站:www.ceqmap.com
网站日志:
/www/wwwlogs/www.ceqmap.com.log
网站 Nginx 配置:
/www/server/web_conf/nginx/vhost/www.ceqmap.com.conf
OpenResty 主配置:
/www/server/openresty/nginx/conf/nginx.conf
PHP 5.3 Nginx 配置:
/www/server/web_conf/php/conf/enable-php-53.conf
==================================================
一、问题现象
==================================================
MW-Panel 经常出现:
负载状态:100%
CPU 使用率:100%
运行堵塞
SSH 检查曾出现:
load average: 2.39, 3.01, 1.37
服务器只有 1 核,因此 Load 长时间超过 1 后,
比较容易出现明显拥堵。
检查发现:
硬盘空间充足;
内存仍有余量;
Swap 基本没有使用;
I/O Wait 很低。
因此本次问题不是硬盘满、内存耗尽或磁盘 I/O 导致。
==================================================
二、最终找到的主要原因
==================================================
检查网站访问日志:
/www/wwwlogs/www.ceqmap.com.log
发现大量搜索引擎、AI、SEO 爬虫集中访问老论坛的动态 PHP 页面,例如:
/forum/forumdisplay.php
/forum/index.php
/forum/stats.php
/bbs/forumdisplay.php
/fddkp/*.php
/rfdkp/*.php
实际发现过:
GPTBot
AhrefsBot
meta-externalagent
Amazonbot
bingbot
其中 Bingbot 尤其明显。
例如曾发现单个 Bingbot IP 在很短时间内连续请求大量:
/forum/forumdisplay.php
这些请求原来返回:
200
意味着请求真正走完整条链路:
爬虫
↓
OpenResty
↓
PHP 5.3
↓
MySQL
老论坛大量页面需要 PHP + MySQL 动态生成。
因此即使单次请求只有 0.01~0.02 秒,
大量并发、连续抓取仍然会明显增加 1 核服务器负载。
==================================================
三、总体处理思路:两层防护
==================================================
不能只采用“发现一个 Bot,就封一个 Bot”的办法。
因为:
1. Bot 种类会不断变化;
2. 以后还可能出现新的 Bot;
3. 部分机器人可能伪装成普通 Chrome;
4. 仅靠 User-Agent 黑名单不可能全部识别。
因此采用两层保护:
【第一层】
已经确认不需要的高负载 Bot
→ OpenResty 直接返回 403
→ 不进入 PHP/MySQL
【第二层】
其他所有 PHP 动态访问
→ 按 IP 限制访问速度
→ 平均 3 次/秒
→ 允许短时间 burst=10
→ 超出后返回 429
→ 不再继续消耗 PHP/MySQL
特别注意:
Google 不加入第一层黑名单!
本站仍然需要 Google 正常抓取和收录。
==================================================
四、第一层:拦截已知高负载 Bot
==================================================
【目的】
对于已经确定不需要的 Bot,
最省服务器资源的办法不是让 PHP 判断,
而是在 OpenResty/Nginx 层直接结束请求。
这样这些请求根本到不了 PHP 5.3 和 MySQL。
【修改文件】
/www/server/web_conf/nginx/vhost/www.ceqmap.com.conf
【主要拦截对象】
OpenAI:
GPTBot
OAI-SearchBot
ChatGPT-User
SEO:
AhrefsBot
SemrushBot
Meta:
meta-externalagent
meta-externalfetcher
facebookexternalhit
Facebot
Amazon:
Amazonbot
Microsoft:
bingbot
BingPreview
Yahoo:
Slurp
Apple:
Applebot
Yandex:
YandexBot
百度:
Baiduspider
字节:
Bytespider
华为:
PetalBot
Common Crawl:
CCBot
【特别保留】
Googlebot
GoogleOther
Google-InspectionTool
Google-Extended
不加入 403 黑名单。
【核心原理】
例如:
if ($http_user_agent ~* "bingbot") {
return 403;
}
意思是:
如果 User-Agent 中出现 bingbot,
OpenResty 立即返回 HTTP 403。
不再执行 PHP。
【实际验证】
原来:
Bingbot → 200 → 约 0.01~0.02 秒
修改后:
Bingbot → 403 → 0.000 秒
例如日志连续出现:
bingbot → 403 → 0.000
说明第一层防护已经成功:
Bingbot
↓
OpenResty
↓
403
×
PHP 5.3
×
MySQL
==================================================
五、第二层第一步:建立 PHP 限速区
==================================================
【目的】
第一层只能处理“认识的 Bot”。
为了防止未知 Bot、伪装浏览器的机器人以及单个 IP
疯狂请求 PHP,需要建立一个通用 PHP 限速机制。
【修改文件】
/www/server/openresty/nginx/conf/nginx.conf
在 http {} 中原有:
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
下面增加:
# PHP 动态页面防拥堵限速区
# 按访问者 IP 统计,每个 IP 平均最多 3 个请求/秒
limit_req_zone $binary_remote_addr zone=php_perip:10m rate=3r/s;
# 超出限速后返回 HTTP 429
limit_req_status 429;
【这一步到底做什么?】
limit_req_zone:
建立一个名为:
php_perip
的限速统计区。
$binary_remote_addr:
按照访问者 IP 分别计算。
rate=3r/s:
每个 IP 平均允许:
3 个请求/秒。
【重要】
做到这里以后,还没有真正限制任何网页。
这一步只是:
“建立限速规则”。
下一步才是:
“让 PHP 使用这个规则”。
==================================================
六、第二层第二步:让 PHP 5.3 使用限速规则
==================================================
【修改文件】
/www/server/web_conf/php/conf/enable-php-53.conf
当前配置:
set $PHP_ENV 1;
location ~ [^/]\.php(/|$)
{
try_files $uri =404;
# PHP 动态页面防拥堵限速
limit_req zone=php_perip burst=10 nodelay;
#fastcgi_pass unix:/tmp/php-cgi-53.sock;
fastcgi_pass MW-UPSTREAM-PHP53;
fastcgi_index index.php;
include fastcgi.conf;
include /www/server/web_conf/php/pathinfo.conf;
}
【这一行是什么意思?】
limit_req zone=php_perip burst=10 nodelay;
zone=php_perip
使用上一节在 nginx.conf 中建立的限速规则。
burst=10
允许短时间额外突发 10 个请求。
原因:
正常用户可能快速点击页面,
浏览器本身也可能短时间连续发出多个请求。
如果完全死卡在每秒 3 次,
可能误伤正常使用。
因此给正常的短时突发留出余量。
nodelay
在 burst 允许范围内:
立即处理,不故意排队等待。
超过“3r/s + burst=10”能够容纳的范围后:
返回:
HTTP 429 Too Many Requests
并阻止超量请求继续进入 PHP。
==================================================
七、为什么第一层和第二层都要保留
==================================================
第一层:
已知 Bot
↓
403
↓
最快、最省资源
例如 Bingbot 即使疯狂请求,
OpenResty 直接 0.000 秒处理掉。
第二层:
未知 Bot / 普通访问 / 未被黑名单识别的机器人
↓
访问 PHP
↓
按 IP 限速
↓
平均 3r/s
burst=10
↓
正常 → PHP 5.3 → MySQL
过快
↓
429
↓
不让超量请求继续压 PHP/MySQL
所以:
403 = 精确拦截已知 Bot
429 = 给剩余 PHP 流量加保险
==================================================
八、Google 的处理原则
==================================================
Google 不加入 Bot 黑名单。
也就是说:
Googlebot 不会因为第一层规则直接得到 403。
但是 Google 访问 PHP 时仍属于 PHP 动态访问,
因此第二层通用限速仍然存在。
这样做的目的:
既保留 Google 正常收录,
又防止任何单个来源在极短时间内把 PHP 5.3 / MySQL 打满。
==================================================
九、每次修改配置必须执行的步骤
==================================================
任何 Nginx/OpenResty 配置修改后:
【第一步:先检查语法】
/www/server/openresty/nginx/sbin/nginx -t
必须看到:
syntax is ok
test is successful
【这一步的目的】
确认配置没有:
拼写错误
括号错误
指令位置错误
参数错误
如果这里报错:
绝对不要 reload。
【第二步:语法正确以后平滑加载】
/www/server/openresty/nginx/sbin/nginx -s reload
【这一步的目的】
让新配置正式生效。
reload 是平滑加载,
不需要为了修改 Nginx 配置直接重启整个 VPS。
==================================================
十、如何确认 Bot 拦截已经生效
==================================================
例如检查 Bingbot:
tail -n 1000 /www/wwwlogs/www.ceqmap.com.log | grep -ai 'bingbot' | tail -20
正常应该看到:
403
以及:
0.000
例如:
bingbot → 403 → 0.000
说明:
请求到 OpenResty 就结束了,
没有继续进入 PHP/MySQL。
==================================================
十一、如何检查 PHP 限速是否触发
==================================================
查看最近 2000 条日志中的 429:
tail -n 2000 /www/wwwlogs/www.ceqmap.com.log | awk '$9==429' | tail -20
统计最近 10000 条有多少个 429:
tail -n 10000 /www/wwwlogs/www.ceqmap.com.log | awk '$9==429' | wc -l
如果:
没有输出
或者:
0
并不代表配置失败。
只是说明:
目前没有任何 IP 的 PHP 请求速度超过限制。
==================================================
十二、服务器再次出现 100% 时如何排查
==================================================
不要一看到 MW-Panel 100% 就:
换 IP
重装系统
恢复备份
升级 PHP
购买新服务器
先查原因。
【1. 查看系统负载】
uptime
例如:
load average: 0.20, 0.55, 0.59
对于当前 1 核服务器属于很轻的状态。
【2. 查看谁正在吃 CPU / 内存】
top
重点观察:
mysqld
php-fpm
openresty
以及:
CPU idle
内存
Swap
I/O wait
【3. 查看最近网站访问】
tail -n 50 /www/wwwlogs/www.ceqmap.com.log
【4. 找访问最多的 IP】
tail -n 10000 /www/wwwlogs/www.ceqmap.com.log | awk '{print $1}' | sort | uniq -c | sort -nr | head -20
【5. 找仍然获得 200 的高频 IP】
tail -n 10000 /www/wwwlogs/www.ceqmap.com.log | awk '$9==200 {print $1}' | sort | uniq -c | sort -nr | head -20
这一步非常重要:
403 已经被挡掉的 Bot 不可怕。
真正值得继续调查的是:
某个 IP 大量请求,
而且仍然获得 200。
【6. 查看某个可疑 IP 到底在访问什么】
grep -a '^IP地址 ' /www/wwwlogs/www.ceqmap.com.log | tail -30
把“IP地址”换成实际地址。
【7. 查看 PHP 限速是否正在发挥作用】
tail -n 10000 /www/wwwlogs/www.ceqmap.com.log | awk '$9==429' | wc -l
==================================================
十三、Snapshot 恢复后的重新部署流程
==================================================
如果以后再次恢复旧 Snapshot,
不要凭记忆乱改。
按照以下顺序恢复。
第 1 步:
uptime
top
目的:
先确认恢复后的服务器基础状态。
第 2 步:
检查高频 User-Agent:
tail -n 10000 /www/wwwlogs/www.ceqmap.com.log | awk -F'"' '{print $6}' | sort | uniq -c | sort -nr | head -20
目的:
确认是否仍有 Bot 在大量访问。
第 3 步:
恢复网站配置中的 Bot 403 黑名单:
/www/server/web_conf/nginx/vhost/www.ceqmap.com.conf
目的:
第一时间把已知高负载 Bot 挡在 PHP 外面。
第 4 步:
nginx -t
完整命令:
/www/server/openresty/nginx/sbin/nginx -t
确认 OK 后:
/www/server/openresty/nginx/sbin/nginx -s reload
第 5 步:
验证例如 Bingbot 是否:
200 → 403
第 6 步:
修改:
/www/server/openresty/nginx/conf/nginx.conf
恢复:
limit_req_zone $binary_remote_addr zone=php_perip:10m rate=3r/s;
limit_req_status 429;
目的:
建立 PHP 按 IP 限速区。
第 7 步:
修改:
/www/server/web_conf/php/conf/enable-php-53.conf
加入:
limit_req zone=php_perip burst=10 nodelay;
目的:
真正让 PHP 请求应用限速。
第 8 步:
再次:
/www/server/openresty/nginx/sbin/nginx -t
确认:
syntax is ok
test is successful
第 9 步:
正式生效:
/www/server/openresty/nginx/sbin/nginx -s reload
第 10 步:
检查:
tail -n 2000 /www/wwwlogs/www.ceqmap.com.log | awk '$9==429' | tail -20
以及:
uptime
==================================================
十四、本次实际处理结果
==================================================
问题严重时曾出现:
load average: 2.39, 3.01, 1.37
第一次处理后曾降到:
load average: 0.92, 0.88, 0.94
后来恢复 Snapshot 后重新部署:
1. Bot 403 黑名单
2. PHP 3r/s + burst=10 + 429
最终再次检查:
load average: 0.20, 0.55, 0.59
最近 2000 条日志没有出现 429。
这说明:
目前服务器负载很低;
已知 Bingbot 等 Bot 已经被 403 提前拦截;
当前正常访问量还没有达到 PHP 通用限速阈值。
==================================================
十五、最终结论
==================================================
本次高负载的主要原因不是:
VPS 损坏
硬盘空间不足
内存耗尽
Swap 不够
磁盘 I/O 故障
而是:
1 核老服务器
+
PHP 5.3 动态网站
+
大量 Bot 集中抓取 PHP 页面
+
PHP/MySQL 被反复调用
最终采用:
第一层:
已知高负载 Bot → 403
第二层:
所有 PHP → 每 IP 平均 3r/s
正常短时突发:
burst=10
超速:
429
Google:
不加入 Bot 403 黑名单
配置修改原则:
先备份 → 修改 → nginx -t → OK → reload
目前结论:
暂时没有必要因为本次问题:
换 IP
重装系统
恢复网站
升级 PHP
升级双核
继续观察实际负载即可。
如果以后再次出现 100%:
先 uptime
→ 再 top
→ 再查访问日志
→ 找高频 200 IP
→ 查 403 / 429
→ 最后根据证据决定怎么处理。
特别备注:
PHP 5.3 是本站老程序兼容性所需,
不能因为本次负载问题随意升级。
本次处理的核心不是“升级 PHP”,
而是尽可能让无意义的高频请求在进入 PHP 5.3 / MySQL 之前就被 OpenResty 拦截。 |