本文系统性拆解Nginx反代502排障的完整实操要点。
上个月一个做金融教育投放的客户在独立部署斗篷系统后,第二天下午开始出现间歇性落地页打不开,监控显示Nginx反代层大量502。他第一反应是服务器被攻击,但登录后CPU和内存都很平稳。问题出在哪?排查后发现是PHP-FPM进程池的默认配置扛不住下午三点的投放高峰,进程耗尽后Nginx拿不到上游响应。这篇就按排障顺序拆开讲:Nginx反代502的信号含义、PHP-FPM进程池参数怎么按业务量调整、部署后怎么自检请求缓冲与超时配置,以及上线前容易漏掉的几个避坑点。
从502信号反推故障层级
502在Nginx反代架构里是一个明确的信号:Nginx作为反向代理收到了来自上游服务(通常是PHP-FPM)的无效响应。注意区分,504是上游响应超时,502是上游根本没给响应或连接被重置。
排查第一步永远是看Nginx错误日志,不要先动配置。典型日志长这样:
BLOCK0
或者:
BLOCK1
第一种说明PHP-FPM的监听队列满了,进程来不及接受新连接。第二种更常见于PHP-FPM进程崩溃或超时被杀。两种日志指向同一个方向:PHP-FPM进程池的资源配置与业务请求量不匹配。
顺带说一句,如果日志里不断出现recv() failed (104: Connection reset by peer),别急着查网络,先看看PHP-FPM的slowlog里有没有记录到执行超时的脚本。
PHP-FPM进程池参数调优顺序
先理解进程池的工作模型。PHP-FPM的pm参数有三种模式:static、dynamic、ondemand。对于斗篷系统这种请求量波动明显的场景,dynamic模式是最稳妥的起点,它允许进程数在min_spare_servers和max_children之间伸缩。
调参顺序应当从max_children开始,这是进程数的硬上限。估算公式不是拍脑袋:
- 单个PHP-FPM进程常驻内存约30-50MB(视加载的扩展和脚本复杂度而定)
- 可用内存的60%-70%预留给PHP-FPM
max_children= 预留内存 ÷ 单进程内存
比如一台4GB内存的服务器,系统和其他服务占用约1GB,剩3GB给PHP-FPM。按单进程40MB算,max_children设为70左右是合理起点。设小了高峰期进程耗尽报502,设大了可能触发OOM Killer。
第二步调request_terminate_timeout。斗篷系统的分流判定脚本如果涉及外部接口调用或规则库热更新,执行时间可能比普通PHP脚本长。默认值0表示不限制,一旦某个请求死循环,进程会被占住直到内存耗尽。建议设为30-60秒,配合slowlog记录超过5秒的慢请求。
第三步才是min_spare_servers和max_spare_servers。这两个值控制空闲进程的伸缩速度,对502的影响不如max_children直接。建议起始值分别设为10和30,观察高峰期的进程数变化再微调。
Nginx反代层的缓冲与超时验证
很多502问题在PHP-FPM侧解决后,还会偶发一些边缘情况。这时要检查Nginx到PHP-FPM的传递配置:
BLOCK2
fastcgi_read_timeout必须大于PHP-FPM的request_terminate_timeout,否则PHP还在执行,Nginx已经等不及返回504了。缓冲配置影响大响应体的传递,斗篷系统的页面版本决策接口返回的HTML如果超过默认缓冲,会触发磁盘临时文件写入,增加响应延迟。
还有一个容易被忽略的点:listen.backlog。PHP-FPM默认的监听队列长度是511,高并发瞬间可能不够用。在php-fpm.conf的listen指令后追加backlog = 1024,同时调整系统的net.core.somaxconn。部署后自检时,可以用ss -lnt | grep 9000检查监听队列的Send-Q是否持续增长。
部署后自检清单与上线避坑
上线前按这个顺序走一遍,能覆盖大部分502相关的服务稳定性问题:
- 压测验证:用
ab或wrk对斗篷系统的分流判定接口做短时压测,观察PHP-FPM进程数和错误日志。目标不是测出极限值,而是确认max_children在预期峰值下不会打满。 - 进程监控:上线后前24小时,每15分钟记录一次
pm.status_path的输出,重点看max children reached计数器是否增长。如果这个值非零,说明进程池已经触顶过,需要上调max_children。 - 日志轮转:确认PHP-FPM的
slowlog和Nginx的error.log都有轮转配置,避免磁盘写满引发的连锁故障。 - 回滚预案:保存调参前的完整配置文件副本,确保配置错误时可以在几分钟内回退。
一个真实的坑:某团队在调优max_children后没改fastcgi_read_timeout,结果高峰期部分请求从502变成了504。原因是PHP执行时间被request_terminate_timeout限制在30秒,但Nginx等待60秒,慢请求堆积后连接数占满。两个超时参数的配合关系在部署后自检时一定要单独验证。
小结
Nginx反代502的核心排查路径是:先看错误日志确定信号类型,再检查PHP-FPM进程池的max_children是否触顶,然后验证Nginx到PHP-FPM的超时与缓冲配置是否匹配,最后用压测和监控确认调整生效。独立部署的优势在于可以自主控制这些参数,但代价是必须理解进程池的工作模型,否则默认配置在投放高峰时很容易成为瓶颈。
常见问题
Nginx反代502和504有什么区别
502是Nginx从PHP-FPM拿不到有效响应,通常是进程崩溃或连接被重置。504是Nginx等待上游响应超过了设定的超时时间。说白了,502是对方不回应,504是对方回应太慢,排查方向一个往进程状态看,一个往执行时间看。
PHP-FPM的pm模式选static还是dynamic
这个要看情况。如果业务流量稳定、服务器资源充足,static模式省去进程创建和销毁的开销,响应更平顺。但斗篷系统的投放流量波动大,dynamic模式能让空闲进程释放资源,更适合成本敏感的小团队。顺带说一句,我一般还会结合ondemand模式做测试环境,生产环境保持dynamic。
max_children设置多少才够用
没有固定数字,按单进程内存占用和可用内存比例来算。4GB服务器从70左右起步,8GB服务器可以到140左右。关键是上线后观察max children reached计数器,只要有增长就说明触顶过,需要上调。
调整PHP-FPM参数后需要重启Nginx吗
不需要。PHP-FPM的配置修改后,用kill -USR2信号平滑重载PHP-FPM主进程即可,Nginx侧如果没改配置就不用动。但改了fastcgi_read_timeout这类Nginx参数,就必须重载Nginx才能生效。
延伸阅读
理解了Nginx反代502的排障路径后,如果还想对比不同跳转插件在架构层面的稳定性差异,可以读这篇针对跳转插件的对比分析:跳转方案选型对比。