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


Posted in PHP onAugust 15, 2011

后来,我通过跟踪发现,这类情况的出现,跟 PHP 的 file_get_contents() 函数有着密切的关系。

大、中型网站中,基于 HTTP 协议的 API 接口调用,是家常便饭。PHP 程序员们喜欢使用简单便捷的 file_get_contents("http://example.com/") 函数,来获取一个 URL 的返回内容,但是,如果 http://example.com/ 这个网站响应缓慢,file_get_contents() 就会一直卡在那儿,不会超时。

我们知道,在 php.ini 中,有一个参数 max_execution_time 可以设置 PHP 脚本的最大执行时间,但是,在 php-cgi(php-fpm) 中,该参数不会起效。真正能够控制 PHP 脚本最大执行时间的是 php-fpm.conf 配置文件中的以下参数: The timeout (in seconds) for serving a single request after which the worker process will be terminated
Should be used when 'max_execution_time' ini option does not stop script execution for some reason
'0s' means 'off'
<value name="request_terminate_timeout">0s</value>

默认值为 0 秒,也就是说,PHP 脚本会一直执行下去。这样,当所有的 php-cgi 进程都卡在 file_get_contents() 函数时,这台 Nginx+PHP 的 WebServer 已经无法再处理新的 PHP 请求了,Nginx 将给用户返回“502 Bad Gateway”。修改该参数,设置一个 PHP 脚本最大执行时间是必要的,但是,治标不治本。例如改成 30s,如果发生 file_get_contents() 获取网页内容较慢的情况,这就意味着 150 个 php-cgi 进程,每秒钟只能处理 5 个请求,WebServer 同样很难避免“502 Bad Gateway”。

要做到彻底解决,只能让 PHP 程序员们改掉直接使用 file_get_contents("http://example.com/") 的习惯,而是稍微修改一下,加个超时时间,用以下方式来实现 HTTP GET 请求。要是觉得麻烦,可以自行将以下代码封装成一个函数。

<?php 
$ctx = stream_context_create(array( 
'http' => array( 
'timeout' => 1 //设置一个超时时间,单位为秒 
) 
) 
); 
file_get_contents("http://example.com/", 0, $ctx); 
?>

当然,导致 php-cgi 进程 CPU 100% 的原因不只有这一种,那么,怎么确定是 file_get_contents() 函数导致的呢?

首先,使用 top 命令查看 CPU 使用率较高的 php-cgi 进程。

top - 10:34:18 up 724 days, 21:01, 3 users, load average: 17.86, 11.16, 7.69 
Tasks: 561 total, 15 running, 546 sleeping, 0 stopped, 0 zombie 
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: 8100996k total, 4320108k used, 3780888k free, 772572k buffers 
Swap: 8193108k total, 50776k used, 8142332k free, 412088k cached 
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
10747 www 18 0 360m 22m 12m R 100.6 0.3 0:02.60 php-cgi 
10709 www 16 0 359m 28m 17m R 96.8 0.4 0:11.34 php-cgi 
10745 www 18 0 360m 24m 14m R 94.8 0.3 0:39.51 php-cgi 
10707 www 18 0 360m 25m 14m S 77.4 0.3 0:33.48 php-cgi 
10782 www 20 0 360m 26m 15m R 75.5 0.3 0:10.93 php-cgi 
10708 www 25 0 360m 22m 12m R 69.7 0.3 0:45.16 php-cgi 
10683 www 25 0 362m 28m 15m R 54.2 0.4 0:32.65 php-cgi 
10711 www 25 0 360m 25m 15m R 52.2 0.3 0:44.25 php-cgi 
10688 www 25 0 359m 25m 15m R 38.7 0.3 0:10.44 php-cgi 
10719 www 25 0 360m 26m 16m R 7.7 0.3 0:40.59 php-cgi

找其中一个 CPU 100% 的 php-cgi 进程的 PID,用以下命令跟踪一下:

strace -p 10747 
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 相关文章推荐
php&amp;java(一)
Oct 09 PHP
windows xp下安装pear
Dec 02 PHP
用PHP读取RSS feed的代码
Aug 01 PHP
php 数组的合并、拆分、区别取值函数集
Feb 15 PHP
PHP中文URL编解码(urlencode()rawurlencode()
Jul 03 PHP
php接口与接口引用的深入解析
Aug 09 PHP
两种设置php载入页面时编码的方法
Jul 29 PHP
php导出csv文件,可导出前导0实例代码
Nov 16 PHP
php根据用户名和手机号查询是否存在手机号码
Feb 16 PHP
PHP使用两个栈实现队列功能的方法
Jan 15 PHP
Laravel使用Queue队列的技巧汇总
Sep 02 PHP
深入浅析安装PhpStorm并激活的步骤详解
Sep 17 PHP
11个PHP 分页脚本推荐
Aug 15 #PHP
PHP版国家代码、缩写查询函数代码
Aug 14 #PHP
PHP动态创建Web站点的方法
Aug 14 #PHP
php程序的国际化实现方法(利用gettext)
Aug 14 #PHP
PHP排序之二维数组的按照字母排序实现代码
Aug 13 #PHP
php中使用Curl、socket、file_get_contents三种方法POST提交数据
Aug 12 #PHP
PHP简洁函数小结
Aug 12 #PHP
You might like
在 PHP 中使用随机数的三个步骤
2006/10/09 PHP
聊天室php&amp;mysql(一)
2006/10/09 PHP
php 读取文件乱码问题
2010/02/20 PHP
ThinkPHP3.2框架使用addAll()批量插入数据的方法
2017/03/16 PHP
php 人员权限管理(RBAC)实例(推荐)
2017/05/24 PHP
CI框架教程之优化验证码机制详解【验证码辅助函数】
2019/04/16 PHP
JAVASCRIPT 对象的创建与使用
2021/03/09 Javascript
如何用jquery控制表格奇偶行及活动行颜色
2014/04/20 Javascript
Node.js中npm常用命令大全
2016/06/09 Javascript
详解Web使用webpack构建前端项目
2017/09/23 Javascript
详解微信小程序中的页面代码中的模板的封装
2017/10/12 Javascript
JS对象和字符串之间互换操作实例分析
2019/02/02 Javascript
详解Vue中的scoped及穿透方法
2019/04/18 Javascript
详解wepy开发小程序踩过的坑(小结)
2019/05/22 Javascript
python实现的守护进程(Daemon)用法实例
2015/06/02 Python
python WindowsError的错误代码详解
2017/07/23 Python
Python实现PS图像抽象画风效果的方法
2018/01/23 Python
Python获取二维矩阵每列最大值的方法
2018/04/03 Python
Pycharm无法显示动态图片的解决方法
2018/10/28 Python
python采集微信公众号文章
2018/12/20 Python
Python 中Django验证码功能的实现代码
2019/06/20 Python
Python 堆叠柱状图绘制方法
2019/07/29 Python
分享一个pycharm专业版安装的永久使用方法
2019/09/24 Python
在python中使用pyspark读写Hive数据操作
2020/06/06 Python
keras中的loss、optimizer、metrics用法
2020/06/15 Python
CSS3之transition实现下划线的示例代码
2018/05/30 HTML / CSS
HTML5 Web Database 数据库的SQL语句的使用方法
2012/12/09 HTML / CSS
HTML5离线应用与客户端存储的实现
2018/05/03 HTML / CSS
英国女性化妆品收纳和家具网站:Beautify
2019/12/07 全球购物
聚美优品的广告词
2014/03/14 职场文书
委托证明模板
2014/09/16 职场文书
旅游局领导班子“四风”问题对照检查材料思想汇报
2014/09/29 职场文书
2015年党务公开工作总结
2015/05/19 职场文书
SQL实现LeetCode(176.第二高薪水)
2021/08/04 MySQL
Python图片验证码降噪和8邻域降噪
2021/08/30 Python
python 学习GCN图卷积神经网络
2022/05/11 Python