PHP-CGI 进程 CPU 100% 与 file_get_contents 函数的关系

[文章作者:张宴本文版本:v1.2最后修改:2010.05.24转载请注明原文链接:http://blog.s135.com/nginx_0day/]

有时候,运行nginx、php-CGI(php-fpm)web服务器的Linux服务器,突然系统负载上升,使用top命令查看,很多php-fpm进程CPU使用率接近100%。后来跟踪发现,这类情况的出现,跟PHP的file_get_contents()函数有着密切的关系。
大、中型网站中,基于http协议的API接口调用,是家常便饭。PHP程序员们喜欢使用简单便捷的file_get_contents("http://example.com")函数来获取一个URL的返回内容,但是,如果http://example.com这个网站响应缓慢,file_get_contents()就会一直卡在那儿,不会超时。
我们知道,在php.ini中,有一个参数max_executiong_time可以设置PHP脚本的最大执行时间,但是,在php-cgi(php-fpm)中,该参数不会起效。真正能够控制PHP脚本最大执行时间的是php-fpm.conf配置文件中的以下参数:
request_terminate_timeout=0
该参数的默认是0秒,也就是说,PHP脚本会一直执行下去。这样,当所有的php-cgi进程都卡在file_get_contents()函数时,这台nginx+php的webserver已经无法再处理新的Php请求了,nginx将给用户返回“502badgateway"错误。修改该参数,设置一个php脚本最大执行时间是必要的,但是治票不治本。例如修改成request_terminate_timeout=30,如果发生file_get_contents()获取网页内容较慢的情况,这就意味着150个php-cgi进程,每秒只能处理5个php请求,webserver同样很难避免”502badgateway“错误。
要彻底解决,只能让Php程序员们改掉直接使用file_get_contents("http://example.com")的习惯,而是稍微修改一下,加个超时时间,用以下方式来实现httpget请求。要是觉得麻烦,可以自行将以下代码封装成一个函数。
<?php
$ctx=stream_context_create(array(
'http'=>array(
'timeout'=>1//设置一个超时时间,单位为秒
)
)
);
file_get_contents("http://example.com/",0,$ctx);
?>
当然导致php-cgi进程CPU100%的原因不只有这么一种,那么,怎么确定是file_get_contents()函数导致的呢?
首先,使用top命令查看CPU使用率较高的Php-cgi进程。

top-10:34:18up724days,21:01,3users,loadaverage:17.86,11.16,7.69
Tasks:561total,15running,546sleeping,0stopped,0zombie
Cpu(s):5.9%us,4.2%sy,0.0%ni,89.4%id,0.2%wa,0.0%hi,0.2%si,0.0%st
Mem:8100996ktotal,4320108kused,3780888kfree,772572kbuffers
Swap:8193108ktotal,50776kused,8142332kfree,412088kcached

PIDUSERPRNIVIRTRESSHRS%CPU%MEMTIME+COMMAND
10747www180360m22m12mR100.60.30:02.60php-cgi
10709www160359m28m17mR96.80.40:11.34php-cgi
10745www180360m24m14mR94.80.30:39.51php-cgi
10707www180360m25m14mS77.40.30:33.48php-cgi
10782www200360m26m15mR75.50.30:10.93php-cgi
10708www250360m22m12mR69.70.30:45.16php-cgi
10683www250362m28m15mR54.20.40:32.65php-cgi
10711www250360m25m15mR52.20.30:44.25php-cgi
10688www250359m25m15mR38.70.30:10.44php-cgi
10719www250360m26m16mR7.70.30:40.59php-cgi
找到其中一个CPU100%的php-cgi进程的PID,用以下命令跟踪一下:
strace-p10747
如果屏幕显示如下:

select(7,[6],[6],[],{15,0})=1(out[6],left{15,0})
poll([{fd=6,events=POLLIN}],1,0)=0(Timeout)
select(7,[6],[6],[],{15,0})=1(out[6],left{15,0})
poll([{fd=6,events=POLLIN}],1,0)=0(Timeout)
select(7,[6],[6],[],{15,0})=1(out[6],left{15,0})
poll([{fd=6,events=POLLIN}],1,0)=0(Timeout)
select(7,[6],[6],[],{15,0})=1(out[6],left{15,0})
poll([{fd=6,events=POLLIN}],1,0)=0(Timeout)
select(7,[6],[6],[],{15,0})=1(out[6],left{15,0})
poll([{fd=6,events=POLLIN}],1,0)=0(Timeout)
select(7,[6],[6],[],{15,0})=1(out[6],left{15,0})
poll([{fd=6,events=POLLIN}],1,0)=0(Timeout)
select(7,[6],[6],[],{15,0})=1(out[6],left{15,0})
poll([{fd=6,events=POLLIN}],1,0)=0(Timeout)
select(7,[6],[6],[],{15,0})=1(out[6],left{15,0})
poll([{fd=6,events=POLLIN}],1,0)=0(Timeout)
select(7,[6],[6],[],{15,0})=1(out[6],left{15,0})
poll([{fd=6,events=POLLIN}],1,0)=0(Timeout)
select(7,[6],[6],[],{15,0})=1(out[6],left{15,0})
poll([{fd=6,events=POLLIN}],1,0)=0(Timeout)
那么,就可以确定是file_get_contents()函数导致的问题了。

你可能感兴趣的:(进程,关系,php-cgi)