别不信,真的有80%的入行1-3年运维或者兼顾PHP服务的后端,第一次遇到PHP-FPM相关的502,第一反应都是“是不是MySQL挂了?是不是带宽不够?是不是代码写得太烂?”那天他们的开发也是这么排查的,先看了MySQL慢查日志——那天C端砍价预热相关的慢查只有3条,都是白天运营后台批量上传砍价商品的后台SQL,和C端流量没关系;又查了阿里云的带宽监控——当天早上临时从5M升成了20M,峰值流量才11M,完全够用;最后临时加了Nginx的error_log debug级别,翻了2分钟才找到真正的罪魁祸首。
给你们看那天我从error_log里扒出来的真实报错片段(稍微脱敏了一下路径和时间):
2026/03/18 20:02:47 [error] 12345#12345: *67890 connect() to unix:/var/run/php-fpm/php83-fpm.sock failed (11: Resource temporarily unavailable) while connecting to upstream
看到了吧?“Resource temporarily unavailable”翻译成大白话就是PHP-FPM的进程数不够用了,或者连接池满了,连Nginx请求都接不住。那天他们的PHP-FPM配置还是阿里云CentOS Stream 9一键安装LNMP(哦对,2026年主流的已经不是CentOS 7了,改成CentOS Stream 9或者Ubuntu 22.04 LTS了,不过一键安装包还是会有坑)给的默认参数,简直是灾难级的配置。
默认配置一般是pm.max_children=20,pm.start_servers=5,pm.min_spare_servers=5,pm.max_spare_servers=10,pm.process_idle_timeout=10s,pm.max_requests=500——先给你们解释一下这些核心参数,但别太官方,用踩过坑的方式说:pm.max_children就是PHP-FPM最多能开多少个worker进程,来处理Nginx抛过来的请求;pm.start_servers是启动时直接开的进程数;pm.min_spare_servers和pm.max_spare_servers是空闲进程的上下限;pm.process_idle_timeout是空闲超过多久就把进程杀掉;pm.max_requests是单个worker进程处理多少个请求后自动重启,避免PHP内存泄漏(虽然PHP 8.3的内存管理已经比7.4好太多了,但还是防不住有些老旧代码或者第三方库瞎搞)。
那天的配置里,pm.max_children=20,pm.max_spare_servers=10——12w的UV,砍价页小程序端是1秒刷新一次看有没有抢到优惠券,C端访问的并发数(用阿里云ARMS实时看的)大概是150左右,20个worker进程,连塞牙缝都不够,直接就全占满了,Nginx连连接都发不进去,自然就跳502了。
接下来就是调优了,调优之前一定要先算好自己的服务器内存能撑得起多少个PHP-FPM worker进程,这是第一个避坑提醒——别瞎抄网上的配置,比如有人说pm.max_children设成500,要是你的服务器只有2G内存,PHP 8.3一个worker进程大概占30M-80M(看你的代码复杂程度,比如带WordPress、Laravel这种框架的会大一点,纯原生PHP的会小一点),2G内存去掉系统、Nginx、MySQL、Redis(那天这家生鲜有Redis但配置得也不好,不过今天先讲PHP-FPM),剩下的大概1.2G左右,1.2G/50M=24个?不对,还要留一点缓冲空间,不然万一MySQL或者Redis突然占了点内存,系统就会OOM(Out Of Memory),直接把PHP-FPM或者MySQL杀掉,那就是更大的灾难了。
算内存的命令也给你们,直接敲就行:
# 查看单个PHP-FPM worker进程的平均内存占用(RSS,实际物理内存)
ps aux | grep php-fpm | grep -v grep | grep -v master | awk '{sum+=$6} END {print "单个PHP-FPM worker平均占用内存:" sum/NR/1024 " MB"}'
查看服务器当前可用的内存(去掉buffers/cache的,真正能给应用用的)
free -m | awk '/Mem:/ {print "可用内存:" $7 " MB"}'
那天这家生鲜的服务器是4核8G的阿里云ECS,WordPress改的小程序后端,单个PHP-FPM worker进程平均占65M左右,可用内存大概4.8G——我个人 留1.8G的缓冲空间(给Redis、MySQL、还有突发的系统任务),剩下的3G/65M≈46,取个整,pm.max_children=45就够了,别贪多。
调优完pm.max_children,接下来就是其他几个参数:pm.process_idle_timeout默认10s有点短,要是流量有波动,比如白天人少,晚上人多,频繁杀进程开进程反而会浪费CPU资源,中小团队线上生产场景下我个人 改成30s或者60s;pm.max_requests默认500太少了,PHP 8.3的内存管理没问题的话,改成5000或者10000都可以,老旧代码的话就先改成2000试试;还有一个很多一键安装包不会开的参数,pm.request_terminate_timeout=30s,这个参数很重要——第二个避坑提醒——要是有某个PHP请求因为死循环、第三方接口超时(那天这家生鲜的砍价页还会调用第三方的短信验证码接口预热?哦对,短信接口那天也有点卡,不过不是主要原因)卡住了,超过30s就自动杀掉这个worker进程,不会让它一直占着内存和CPU,拖死整个服务。
给你们看那天我给他们改的完整PHP-FPM配置片段(还是稍微脱敏了一下路径),文件是/etc/php-fpm.d/www.conf:
[www]
user = nginx
group = nginx
listen = /var/run/php-fpm/php83-fpm.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0660
pm = dynamic # 中小团队线上生产场景下用dynamic就够了,static适合流量非常稳定的大公司
pm.max_children = 45
pm.start_servers = 15 # 一般设成pm.max_children的1/3左右
pm.min_spare_servers = 10 # 一般设成pm.start_servers的2/3左右
pm.max_spare_servers = 25 # 一般设成pm.start_servers的1.5倍左右

pm.process_idle_timeout = 60s
pm.max_requests = 8000
pm.request_terminate_timeout = 30s
pm.request_slowlog_timeout = 2s # 这个参数也 开,超过2s的请求会写到慢查日志里,方便排查代码问题
slowlog = /var/log/php-fpm/www-slow.log
php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
php_value[memory_limit] = 256M # 老旧代码可以改成128M,Laravel这种大框架可以改成512M,但别超过单个worker进程的平均内存太多
改完配置之后,一定要先检查一下PHP-FPM的配置有没有语法错误,别直接重启,不然重启失败服务就彻底挂了,用这个命令:
php-fpm -t
要是显示“test is successful”就没问题,然后再重启PHP-FPM,CentOS Stream 9用systemctl restart php-fpm.service,Ubuntu 22.04 LTS用systemctl restart php8.3-fpm.service,重启完之后用top或者htop看看PHP-FPM的进程数对不对,用free -m看看内存有没有压力。
那天重启完之后,第二天晚上再撒券,虽然UV冲到了14w,并发数大概180左右,但没有出现一次502错误,阿里云ARMS的响应时间也从之前的2s-5s降到了300ms-800ms,客服后台的投诉只有3条(都是因为用户自己的网络问题),运营终于笑了。
对了,还有一个可能导致PHP-FPM 502的小坑,很多新手会忽略——就是Nginx的fastcgi_buffer_size和fastcgi_buffers参数,要是响应的内容太大(比如首页带了很多图片的base64编码,或者Laravel的错误页面),Nginx的缓冲区不够用,也会跳502错误。那天这家生鲜的base64图片还好,不过我也顺便给他们调了一下Nginx的配置,文件是/etc/nginx/conf.d/default.conf里的server块:
location ~ .php$ {
fastcgi_pass unix:/var/run/php-fpm/php83-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
}
调完这个之后,再检查一下Nginx的配置有没有语法错误,用nginx -t,没问题的话就重启Nginx,systemctl restart nginx.service。
最后 一下吧,PHP-FPM相关的502错误,80%都是因为pm.max_children不够用,剩下的20%是因为pm.request_terminate_timeout没开、Nginx的fastcgi缓冲区不够用、或者代码有问题。调优的时候一定要先算好内存,别瞎抄网上的配置,留一点缓冲空间,开慢查日志和请求终止超时,遇到问题先看Nginx和PHP-FPM的错误日志,别瞎重启。
你们在运维工作中有没有遇到过类似的PHP-FPM 502坑?欢迎在评论区分享你的排查经验!

评论列表 (0条):
加载更多评论 Loading...