为什么PHP项目运行报错502,Nginx + php-fpm 出现502错误分析

本文详细分析了在Nginx上运行PHP项目时出现502 Bad Gateway错误的原因,包括php-fpm进程数不足和PHP脚本执行超时,并提供了相应的解决策略,如调整php-fpm配置和控制程序超时。
部署运行你感兴趣的模型镜像

在nginx上跑php容易出现502错误,很多人遇到过这个问题,到底是什么原因导致的呢?

出现502错误后,又有哪些解决办法呢?

本文源起于前段时间处理无线浏览器的一次502报警后总结得来,希望能为大家提供参考。

从报警说起

现象

1、打开静态页面很快,如无线浏览器的更新日志页面,请求云控接口却很慢,报502错误。

2、最开始一周报警1-2次,后来发展到一天报警1次。

处理过程

1、查看错误日志,发现502错误的响应时间都比较长,均为21s左右。见下图:

ba5fbfd836103d0576d7ad07ce29e373.png

日志中请求的是云控接口,就是一个读取配置文件然后吐出数据的过程,正常情况下响应时间应该是非常短的。

因此直观猜测是php进程堵死了,云控请求过来时php进程不够用了,从而导致502错误。

使用 netstat -napo | grep "php-fpm" | wc -l 查看了一下当前fastcgi进程个数, 发现已达到php-fpm.conf中pm.max_children的设置值。

重启php-fpm后,报警得以解除,但这并没有找到问题根源,php进程为什么会堵死呢?

2、猜测进程堵死的原因应该是大量的慢请求导致的。在/usr/local/php/etc/php-fpm.conf文件中,发现有关于慢日志的设置:

slowlog = /data/php/log/$pool.log.slow

request_slowlog_timeout = 10

request_terminate_timeout = 0

request_terminate_timeout配置的是一个php脚本的最大执行时间,默认是0s。

0s的含义是让php脚本一直执行下去而没有时间限制。

当再次收到报警时,查看php的slow log,果然发现发现大量执行时间超过10s的脚本。慢日志如下:

11-Aug-2014 08:34:53 pool www pid 8683

script_filename = /home/q/system/api.mse.360.cn/src/www/api//index.php0x00000000013d0420 curl_exec() /home/q/system/api.mse.360.cn/src/application/models/bizservice/icon_svc.php:291

0x00000000013cff98 httpRequest() /home/q/system/api.mse.360.cn/src/application/models/bizservice/icon_svc.php:203

0x00000000013cf710 getTitleBySite() /home/q/system/api.mse.360.cn/src/application/models/bizservice/icon_svc.php:33

0x00000000013cf260 getIconAndTitleBySite() /home/q/system/api.mse.360.cn/src/application/controllers/api/IconController.php:25

0x00000000013cf0c0 addSiteAction() /home/q/php/QFrameSmarty/web/QFrameaction.php:46

0x00000000013ce9a0 dispatch() /home/q/php/QFrameSmarty/web/QFrameweb.php:176

0x00000000013ce4b0 dispatch() /home/q/php/QFrameSmarty/web/QFrameweb.php:150

0x00000000013ce190 runController() /home/q/php/QFrameSmarty/web/QFrameweb.php:129

0x00000000013ce018 processRequest() /home/q/php/QFrameSmarty/web/QFrameweb.php:37

0x00000000013cdc50 run() /home/q/system/api.mse.360.cn/src/www/api/index.php:15

终于找到php进程堵死的根源:调用添加icon到浏览器主屏这个接口时,大量的curl抓取页面title执行超时。

慢请求过多导致php进程不能及时释放,所以触发报警。

添加curl的执行超时,上线代码后,php进程终于治“堵”成功,nginx+php-fpm的502报警彻底解除。

为什么会出现502

一般而言,nginx出现502有很多原因,但大都可以归结为资源数量不够用,也就是说后端php-fpm处理有问题。

nginx将正确的客户端请求发给后端的php-fpm进程,由于php-fpm进程的问题导致不能正确解析php代码,最终返回502错误。

1、php-fpm进程数不够用

nginx和fastcgi是通过socket进行连接的,一个php-fpm进程在同一时刻只能响应一个用户请求。它能处理的最大请求数由php-fpm.conf中如下配置决定:

listen.backlog = 128

pm.max_children = 256

这是我们线上服务器的配置,也就是说php-fpm能处理的最大请求数为384。

当客户端的请求数超过这个值时,再试图发送请求会收到Resource temporarily unavailable,这时nginx就会报502错误。

上面提到的业务报警就是由于php-fpm进程数不够用而导致的。

大量慢请求占据了php进程,php-fpm进程数很快达到峰值。php-fpm被卡死不能处理新的请求时,nginx并不会收到请求被拒绝的类似信息,而是把请求积压在socket上;

此时浏览器所得到的响应就是一个“正在等待响应”的提示,除非socket报错或浏览器关闭否则等待永远不会停止。

当积压数超过backlog的设置值时,高峰时间大量的云控请求就会触发服务端的502报警。

为了验证这一结论,在测试服务器上修改php-fpm.conf文件中的配置进行一个简单测试。

修改php-fpm.conf的相关配置如下:

listen.backlog = 8

pm = dynamic pm.max_children = 8

pm.min_spare_servers = 2

pm.max_spare_servers = 4

重启php-fpm,客户端发送多于16个请求,每个请求脚本都sleep 10s。采用http_load进行压测,结果如下:

21 fetches, 25 max parallel, 18044 bytes, in 30.0058 seconds

859.238 mean bytes/connection

0.699864 fetches/sec, 601.35 bytes/sec

msecs/connect: 0.472571 mean, 0.637 max, 0.229 min

msecs/first-response: 17094.7 mean, 22743.7 max, 10002.3 min

5 bad byte counts

HTTP response codes:

code 200 – 16

code 502 – 5

从压测结果可以看到,php-fpm能处理的最大请求数应为16,超出这个值的请求会报502错误。

2、php脚本执行超时

在php.ini和php-fpm.conf中分别有这样两个配置项:max_execution_time和request_terminate_timeout。

文档中对其注释如下:;

The timeout for serving a single request after which the worker process will

; be killed. This option should be used when the 'max_execution_time' ini option

; does not stop script execution for some reason. A value of '0' means 'off'.

; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)

; Default Value: 0

;request_terminate_timeout = 0

这两项都是用来配置一个php脚本的最大执行时间的,默认为0s。

当超过这个时间时,php-fpm不仅会终止脚本的执行,还会终止执行脚本的worker进程。

当Nginx会发现与自己通信的socket连接断掉了,以为上游服务器挂了,于是返回给客户端502错误。

修改php-fpm.conf配置如下:

request_terminate_timeout = 10

重启php-fpm,写一个php脚本,sleep 20s。打开浏览器访问测试页面,执行该php脚本验证一下,结果如下图:

485f748973bae01dbceecc00ca91eee4.png

由于php脚本执行时间超过配置规定时间,进程被异常终止,所以nginx返回502,验证了上面的结论。

如何避免502

如果服务器并发量非常大,那只能先增加机器,然后按以下方式优化会取得更好效果;但如果并发不大却出现502,一般都可以归结为上述两个原因,php-fpm进程数不够用和php脚本执行超时。

1、增加php-fpm进程数

使用 netstat -napo | grep "php-fpm" | wc -l 查看一下当前fastcgi进程个数。

如果个数接近 php-fpm.conf里配置的pm.max_children上限,就需要调高进程数。

因为max_children设置的较小,那么fastcgi就会“很累”,处理速度也很慢,等待的时间也较长。

但设置max_children也会受到机器资源的限制,因此也并不能无限增加,增大进程数,内存占用也会相应增大。

正常情况下每个php-fpm进程所耗费的内存在20M左右,因此我们线上服务器的配置基本上是最佳的。

2、调整request_terminate_timeout

request_terminate_timeout可以根据服务器的性能进行设定,一般来说性能越好你可以设置越高。

在我们的线上服务器建议直接使用默认值0s就可以了,这样能够避免php进程被异常终止而导致502错误。

可以看到,增加php-fpm进程数和调整request_terminate_timeout均属于配置问题。

在我们的线上环境,相关配置基本上已达到最优,此时要避免502最最重要的还是要控制好程序中的超时。

curl、file_get_contents等函数都要设置超时时间,mysql慢查询请求也要尽量避免。

总结一下

1、nginx出现502错误一般都是后端的php-fpm出问题引起的。

2、pm.max_children不能无限增加,要视机器资源而定。

3、php-fpm.conf中request_terminate_timeout最好采用默认值。

4、大量的慢请求往往才是502的罪魁祸首,所以请一定要管好你程序中的超时。

您可能感兴趣的与本文相关的镜像

Qwen3-8B

Qwen3-8B

文本生成
Qwen3

Qwen3 是 Qwen 系列中的最新一代大型语言模型,提供了一整套密集型和专家混合(MoE)模型。基于广泛的训练,Qwen3 在推理、指令执行、代理能力和多语言支持方面取得了突破性进展

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值