朕喜技术(ZhenxiJS)
2026.08.29
1 分钟阅读

Nginx 反向代理 PHP-FPM 返回 502 Bad Gateway:并发慢请求打满 Worker 池的排查与修复

日期:2026-08-29 | 技术栈:Nginx、PHP-FPM、FastCGI、Docker、docker-compose 报错关键字:502 Bad Gateway、504 Gateway Time-out、server reached pm.max_children setting、upstream timed out、value is NULL for a ZEND_INI_PARSER_ENTRY、host not found in upstream

一、问题背景

生产环境架构为 客户端 → Nginx(80) → PHP-FPM(9000),Nginx 通过 FastCGI 把 .php / /api/ 请求转发给后端 PHP-FPM。

上线后当多个用户同时访问一个需要 5 秒才能返回的慢接口 /api/slow.php 时,几乎所有 PHP 请求都开始返回 502 Bad Gateway / 504 Gateway Time-out,只有静态文件 /static/hello.txt 正常。

二、复现步骤

cd scenario/2026-08-29-0000
docker compose up -d

# 1) 正常请求(应能通)
curl http://localhost:8080/
curl http://localhost:8080/api/index.php

# 2) 10 个并发慢请求触发故障
for i in $(seq 1 10); do
  curl -s -o /dev/null -w "req-$i: %{http_code}\n" http://localhost:8080/api/slow.php &
done
wait

# 3) 随后访问快速接口也失败
curl -i http://localhost:8080/api/index.php

# 4) 看日志
docker compose logs nginx
docker compose logs php-fpm

复现输出:并发慢请求全部 504,随后快速接口也是 504,php-fpm 日志出现 WARNING: [pool www] server reached pm.max_children setting (2), consider raising it。

三、根本原因

两个层面叠加导致:

  1. Nginx 超时太短:fastcgi_read_timeout 2s,而慢接口需处理 5s。Nginx 等不到响应即返回 504 Gateway Time-out(upstream timed out)。
  2. PHP-FPM Worker 池太小 + 无兜底:pm.max_children = 2,同时只能处理 2 个请求。慢请求把 2 个 worker 全部占住,后续所有 PHP 请求(包括快速接口)排队/被拒,Nginx 表现为 502/504。
    • 无 request_terminate_timeout → 超慢请求不会被杀掉。
    • 无 pm.max_requests → worker 长期不重启,易内存泄漏/僵死。

额外环境坑:PHP-FPM 不支持 # 注释

初始 php/www.conf 使用 # 作为注释,导致 php-fpm 直接启动失败:

ERROR: [/usr/local/etc/php-fpm.d/www.conf:1] value is NULL for a ZEND_INI_PARSER_ENTRY
ERROR: FPM initialization failed

进而 Nginx 报 host not found in upstream "php-fpm"。PHP-FPM 池配置应使用 ; 注释,# 不被其 INI 解析器支持。

四、解决办法

修改 php/www.conf(PHP-FPM worker 池)

; 注释必须用 ';',不能用 '#'
[www]
listen = 9000
pm = dynamic
pm.max_children = 16          ; 2 -> 16,容纳更多并发
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
request_terminate_timeout = 30s   ; 杀掉超过 30s 的慢请求
pm.max_requests = 500             ; worker 处理 500 次后重启,防泄漏

修改 nginx/default.conf(FastCGI 反代)

location ~ \.php$ {
    fastcgi_pass php-fpm:9000;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    fastcgi_keep_conn on;          ; 复用上游连接
    fastcgi_read_timeout 60s;      ; 2s -> 60s,> 慢接口 5s
    fastcgi_send_timeout 60s;
    fastcgi_connect_timeout 5s;
    fastcgi_buffer_size 16k;
    fastcgi_buffers 8 16k;
}

五、验证

修复并重启后:

docker compose down && docker compose up -d

# 10 个并发慢请求:全部 200,耗时约 5s
for i in $(seq 1 10); do curl -s -o /dev/null -w "slow-%{http_code} " http://localhost:8080/api/slow.php & done; wait; echo

# 慢请求处理中访问快速接口:全部 200
for i in $(seq 1 5); do curl -s -o /dev/null -w "fast-%{http_code} " http://localhost:8080/api/index.php & done; wait; echo

# 50 并发压测:全部 200
for i in $(seq 1 50); do curl -s -o /dev/null -w "req-%{http_code} " http://localhost:8080/api/slow.php & done; wait; echo

# 无 502/504
docker compose logs nginx | grep -E '" (502|504) ' || echo "无 502/504"
docker compose logs nginx | grep -c "upstream timed out" || echo "0 个 upstream 超时"

结果:并发慢请求、快速接口、50 并发压测全部返回 200;nginx 日志无 upstream timed out,无 502/504。

六、经验与避坑

  1. 502 vs 504:504 Gateway Time-out = Nginx 等上游超过 fastcgi_read_timeout;502 Bad Gateway = 上游连接被拒 / worker 池耗尽。看 nginx error.log 关键字即可区分。
  2. server reached pm.max_children setting 是 worker 池打满的最直接信号,出现即应调大 pm.max_children 或优化慢请求。
  3. PHP-FPM 池配置注释必须用 ;,# 会报 value is NULL,这是最容易忽略的坑。
  4. 慢请求治理三板斧:调大 fastcgi_read_timeout、调大 pm.max_children、设置 request_terminate_timeout + pm.max_requests。三者配合既能承载并发,又能兜底杀超时、防内存泄漏。
  5. 开启 fastcgi_keep_conn 可复用 FastCGI 连接,减少高并发下的握手开销。
  6. 排查时先做最小复现:单独 docker run ... php-fpm -t 验证配置语法,再用 curl 并发脚本定位性能问题,避免环境问题与业务问题相互干扰。

版权声明

本文为博主原创文章,遵循 CC BY-NC-SA 4.0 许可协议。 转载请注明出处:http://localhost/blogs/2026-08-29-0009-nginx-php-fpm-502-bad-gateway

分享这篇文章