PHP‑FPM 高负载下 502 Bad Gateway 排查与完整修复实录
PHP‑FPM 高负载下 502 Bad Gateway 排查与完整修复实录
摘要
线上PHP业务偶发502网关错误,Nginx报错connect() to unix:/run/php‑fpm.sock failed (11: Resource temporarily unavailable),网页间歇性无法访问,重启php‑fpm后短暂恢复,过一段时间故障复现。本文完整记录复现步骤、日志定位、参数调优、内核参数修改、压力验证全流程,适合宝塔、自建LNMP生产环境参考。

问题现象
业务服务器部署LNMP环境,使用Unix socket方式Nginx对接PHP‑FPM。日常访问正常,业务流量上涨之后,部分用户访问网站直接抛出502 Bad Gateway。刷新页面有时候可以正常打开,有时候持续报错。手动执行systemctl restart php‑fpm重启服务,网站立刻恢复正常,但是运行数小时之后故障再次出现。
查看Nginx错误日志:
2026/08/10 14:22:30 [error] 12340: 5678 connect() to unix:/run/php‑fpm.sock failed (11: Resource temporarily unavailable) while connecting to upstream, client: 113.xx.xx.xx, server: test.example.com, request: "GET /index.php HTTP/1.1", upstream: "fastcgi://unix:/run/php‑fpm.sock:", host: "test.example.com"
PHP‑FPM日志中可以看到警告信息:
WARNING: [pool www] server reached pm.max_children setting (20), consider raising it
复现步骤
- php‑fpm配置文件
php‑fpm.conf中进程管理模式使用dynamic,pm.max_children=20,pm.start_servers=5,pm.min_spare_servers=2,pm.max_spare_servers=8。 - Nginx fastcgi连接后端使用unix domain socket,不使用TCP端口。
- 使用压测工具模拟并发请求,并发数设置30‑50持续访问php接口。
- 持续压测3‑5分钟,开始出现大量502报错,日志输出资源临时不可用。
重启php‑fpm服务,故障消失,再次压测问题复现。
根因分析
php‑fpm进程池数量上限不足
pm.max_children定义fpm最大工作进程,并发请求超过最大进程数,新的请求无法分配工作进程,Nginx连接fpm socket直接失败,返回502。日志明确提示达到max_children上限,这是最直接诱因。进程管理参数配置不合理
dynamic模式下,pm.max_spare_servers设置过小,流量上来之后无法快速扩容足够空闲进程,请求排队堆积。很多宝塔面板默认参数偏向小流量测试环境,直接搬到高并发生产环境直接触发瓶颈。Unix socket队列缓冲耗尽
就算进程足够,socket监听队列backlog过小,瞬间大量请求涌入,队列满,新连接直接被丢弃,同样报11资源临时不可用。Nginx、php‑fpm各自都有backlog配置项,很多人会忽略这个参数。PHP脚本执行慢导致进程长期占用
部分业务PHP接口数据库查询慢、外部HTTP请求阻塞,单个fpm进程被长时间占用无法释放。就算设置很大max_children,如果脚本阻塞,进程全部卡死,依旧会出现502。单纯调大进程数治标不治本。文件权限与selinux隐性问题
socket文件权限错误、SELinux开启状态下,Nginx没有权限访问socket,也会出现502,该故障和并发导致的502日志表现相似,需要区分。分步修复方案
步骤1:修改php‑fpm进程池配置
打开/etc/php/8.1/fpm/pool.d/www.conf(宝塔环境路径一般为/www/server/php/81/etc/php‑fpm.conf)
; 切换模式,大流量推荐ondemand或者dynamic,服务器内存充足优先dynamic
pm = dynamic
; 根据服务器内存计算,单个php进程占用20‑40M内存,8G服务器建议max_children设置80‑120
pm.max_children = 100
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 30
; 设置进程空闲多久自动回收,防止内存泄漏
pm.process_idle_timeout = 60s
; 单进程最多处理多少请求之后自动销毁重启,规避php内存泄漏
pm.max_requests = 500
; 调整socket backlog队列长度
pm.backlog = 2048
注意:不要盲目把max_children设置极大,100个进程每个占用30M内存,就要消耗3G内存,超出服务器物理内存会触发OOM杀死进程,引发更多故障。
步骤2:Nginx侧调整fastcgi相关参数
nginx站点配置http块或者server块内修改fastcgi参数
fastcgi_connect_timeout 10s;
fastcgi_send_timeout 30s;
fastcgi_read_timeout 30s;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
; nginx socket backlog,和php‑fpm backlog匹配
listen 80 backlog=2048;
很多人只调php‑fpm,忽略nginx的backlog,瞬间高并发场景下队列依旧会打满。
步骤3:系统内核参数调优
编辑/etc/sysctl.conf,修改网络队列参数
net.core.somaxconn = 2048
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
生效内核参数:
sysctl -p
步骤4:业务层面优化,解决脚本阻塞问题
调大进程数只是兜底方案,真正根源是慢脚本。
- 开启php‑fpm慢日志,定位耗时脚本
slowlog = /var/log/php8.1‑fpm‑slow.log
request_slowlog_timeout = 2s
执行systemctl reload php8.1‑fpm重载配置。慢日志会记录执行超过2秒的PHP调用栈,快速找到慢接口。
- 优化慢SQL,增加数据库索引;业务外部接口调用增加超时时间,禁止无超时的http请求。
耗时任务迁移到消息队列异步处理,不要在PHP网页请求里面做大量同步计算。
步骤5:权限与安全检查
确认socket文件所属用户组,nginx运行用户需要和php‑fpm pool用户保持一致。
listen.owner = www‑data
listen.group = www‑data
listen.mode = 0660
关闭SELinux或者配置对应权限,测试命令setenforce 0临时关闭验证是否为selinux干扰。
步骤6:重载配置,压力测试验证
平滑重载fpm,不中断业务
systemctl reload php8.1‑fpm
检查配置语法是否错误
php‑fpm8.1 -t
nginx -t
systemctl reload nginx
使用压测工具模拟并发访问,持续5‑10分钟,不再出现502错误,查看php‑fpm日志不再输出max_children警告。观察服务器内存占用,确认不会内存溢出。
后续监控告警建议
- 监控php‑fpm活跃进程数量,接近max_children阈值设置告警。
- 监控Nginx502错误率,一旦上涨推送告警。
定期巡检php慢日志,持续优化业务接口性能。
踩坑总结
- 遇到502不要上来就直接重启服务,优先看Nginx和php‑fpm两套日志区分故障类型。
- 502不一定完全是php‑fpm进程不够,脚本阻塞、socket队列、内核参数、selinux都会引发同类报错。
- 参数配置要结合服务器内存,max_children不是越大越好,过大引发OOM会直接服务宕机。
- 配置修改优先使用reload平滑重载,避免restart直接中断线上业务。
凡尘版权
友情链接:凡尘博客
凡尘博客文章|凡尘博客文摘
凡尘影院
凡尘乡音|凡尘街坊
凡尘博客|雨落凡尘博客|羽落凡尘博客
凡尘博客|雨落凡尘博客|羽落凡尘博客