背景
生产环境中的 Debian 服务器(Apache 2.4.38 + MySQL)上运行着若干 WordPress 及自定义 PHP 应用。近期频繁出现 Cloudflare 522 错误(Connection timed out),但服务器 CPU、内存负载正常,Apache 和 MySQL 服务也都在运行。用户访问网站时随机出现无法打开的情况,影响了业务可用性。
什么是 522 错误?
522 是 Cloudflare 特有的错误码,表示 Cloudflare 边缘节点尝试与源服务器建立 TCP 连接后,在允许的时间内未收到完整的 HTTP 响应。Cloudflare 等待源服务器响应的时间大约为 19 秒(连接建立)到 90 秒(数据接收)。
关键认知:522 不代表服务器宕机或资源爆满,而是 源服务器未能及时响应。即使 CPU、内存看起来正常,仍然可能出现 522。
排查过程
1. 初步检查(排除常见原因)
- 防火墙:
iptables -L无 DROP/REJECT 规则,Cloudflare IP 未被拦截。 - MySQL:
max_connections=151,当前连接数仅 10,无异常。 - KeepAlive:已开启(
KeepAlive On,超时 5 秒),配置合理。 - 网络连接状态:
ss -tan显示 ESTAB 连接数高达 428,而 TIME-WAIT 仅 250+,未耗尽临时端口。 - 系统日志:无 OOM、无内核错误。
表面上看一切“正常”,但 522 依然发生。
2. 关键发现:Apache 进程数远低于并发连接数
执行 ps aux | grep apache2 | wc -l 显示 Apache 进程数仅 152,而并发 ESTAB 连接数为 428。
这意味着有大量 TCP 连接处于 ESTABLISHED 状态,但 Apache 没有足够的进程来同时处理它们。
3. 检查 Apache 并发限制(MPM prefork)
查看 MPM 配置 /etc/apache2/mods-available/mpm_prefork.conf:
apache
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxRequestWorkers 150 # 最大并发处理进程数
MaxConnectionsPerChild 0
</IfModule>
MaxRequestWorkers 被设置为 150,意味着 Apache 最多只能同时处理 150 个请求。
当瞬时并发超过 150 时,新请求会进入排队队列;如果队列在 Cloudflare 超时时间内未能被处理,Cloudflare 就返回 522。
4. 更深层限制:ServerLimit 默认为 256
尝试直接提高 MaxRequestWorkers 到 384 后,执行 apache2ctl configtest 出现警告:
text
AH00180: WARNING: MaxRequestWorkers of 384 exceeds ServerLimit value of 256 servers, decreasing MaxRequestWorkers to 256.
原来 Apache prefork 模式下还有一个 ServerLimit 指令,它定义了 MaxRequestWorkers 理论上限,默认值为 256。因此即使配置 384,实际生效的也只有 256。
5. 其他辅助问题
.htaccess中存在语法错误:<RequireAll not allowed in <FilesMatch> context>,导致部分请求被拒绝。- 日志中出现
proxy:error (32)Broken pipe,表示反向代理连接中断(本环境未使用 PHP-FPM,该错误来自其他代理配置)。 - 系统未配置 swap 分区,且存在 swap 设备挂载失败的错误,存在内存耗尽风险。
这些虽然不是 522 的根本原因,但会加重服务不稳定性,增加触发超时的概率。
解决方案
核心修复:调整 Apache MPM prefork 的 ServerLimit 和 MaxRequestWorkers
编辑 /etc/apache2/mods-available/mpm_prefork.conf:
apache
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
ServerLimit 384 # 必须 >= MaxRequestWorkers
MaxRequestWorkers 384
MaxConnectionsPerChild 0
</IfModule>
注意:
ServerLimit必须出现在MaxRequestWorkers之前。- 修改后需要重启 Apache 才能生效。
- 根据服务器内存调整数值:prefork 模式下每个进程约消耗 30~50MB,384 个进程约需 12~19GB。本例服务器总内存 31GB,可用 16GB,设定 384 合理。
重启并验证:
bash
sudo systemctl restart apache2 ps aux | grep apache2 | wc -l # 应接近 384 apache2ctl -S | grep MaxRequestWorkers # 确认生效
辅助优化
- 修复
.htaccess语法错误:
将<FilesMatch "\.php$"><RequireAll>...</RequireAll></FilesMatch>改为<FilesMatch "\.php$"> Require all granted </FilesMatch>。 - 处理
proxy:error:
确认无反向代理需求后,禁用mod_proxy模块:bashsudo a2dismod proxy proxy_http sudo systemctl restart apache2 - 创建 swap 分区(避免内存不足):bashsudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo ‘/swapfile none swap sw 0 0’ | sudo tee -a /etc/fstab
效果验证
修改后,Apache 进程数从 150 提升至 384,能够匹配高峰期的 400+ 并发连接。监控显示:
ESTAB连接数与 Apache 进程数基本持平。- 网站不再出现 Cloudflare 522 错误。
- 用户访问恢复流畅,服务稳定。
经验总结
- 522 错误的首要排查方向是源服务器的请求处理能力,而非仅仅看 CPU/内存。
- Apache prefork MPM 下,
MaxRequestWorkers和ServerLimit是两个关键参数,后者常被忽略。修改时必须同时调整。 - 通过
ps和ss对比进程数与 ESTAB 连接数,能快速判断是否达到并发上限。 - 不要忽视配置语法错误和 swap 缺失,它们会放大主问题的影响。
- Cloudflare 日志中的
ray_id和源服务器错误日志结合分析,能精准定位超时发生的环节。
附录:常用排查命令
bash
# 查看 Apache 当前进程数 ps aux | grep apache2 | wc -l # 查看 80 端口 ESTABLISHED 连接数 ss -tan state established sport = :80 | wc -l # 查看 MPM 配置及生效值 apache2ctl -S | grep -i "maxrequestworkers" # 测试配置文件语法 apache2ctl configtest # 实时监控对比 watch -n 2 'echo "Procs: $(ps aux | grep apache2 | wc -l)"; echo "ESTAB: $(ss -tan state established sport = :80 | wc -l)"'
希望这篇文章能帮助遇到类似问题的同行快速定位并解决 Cloudflare 522 错误。
讨论 / DISCUSS
还没有留言,来留下第一条评论吧!