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也可以?成Shell Script
Oct 09 PHP
PHP中str_replace函数使用小结
Oct 11 PHP
PHP 分页原理分析,大家可以看看
Dec 21 PHP
PDO版本问题 Invalid parameter number: no parameters were bound
Jan 06 PHP
php全角字符转换为半角函数
Feb 07 PHP
PHP中获取时间的下一周下个月的方法
Mar 18 PHP
PHP获取栏目的所有子级和孙级栏目的ID号示例
Apr 01 PHP
smarty内置函数section的用法
Jan 22 PHP
PHP+ajax分页实例简析
Dec 07 PHP
简单谈谈PHP中strlen 函数
Feb 27 PHP
PHP正则匹配反斜杠'\'和美元'$'的方法
Feb 08 PHP
php可变长参数处理函数详解
Feb 22 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获取根域名方法汇总
2014/10/28 PHP
laravel框架路由分组,中间件,命名空间,子域名,路由前缀实例分析
2020/02/18 PHP
js加解密 脚本解密
2008/02/22 Javascript
详解JavaScript中undefined与null的区别
2014/03/29 Javascript
jQuery关键词说明插件cluetip使用指南
2015/04/21 Javascript
浅谈jQuery构造函数分析
2015/05/11 Javascript
JS处理json日期格式化问题
2015/10/01 Javascript
Jquery实现纵向横向菜单
2016/01/24 Javascript
Bootstrap网格系统详解
2016/04/26 Javascript
AngularJS 依赖注入详解和简单实例
2016/07/28 Javascript
Vue服务端渲染和Vue浏览器端渲染的性能对比(实例PK )
2017/03/31 Javascript
layui获取多选框中的值方法
2018/08/15 Javascript
VUE项目初建和常见问题总结
2019/09/12 Javascript
如何优雅地在Node应用中进行错误异常处理
2019/11/25 Javascript
vue 解决IOS10低版本白屏的问题
2020/11/17 Javascript
[00:03]DOTA2新版本PA至宝展示
2014/11/19 DOTA
[50:59]2018DOTA2亚洲邀请赛 4.7 总决赛 LGD vs Mineski第四场
2018/04/10 DOTA
Python+OpenCV让电脑帮你玩微信跳一跳
2018/01/04 Python
Python中常用的8种字符串操作方法
2019/05/06 Python
pandas 层次化索引的实现方法
2019/07/06 Python
TensorFLow 变量命名空间实例
2020/02/11 Python
python 使用事件对象asyncio.Event来同步协程的操作
2020/05/04 Python
Windows下Sqlmap环境安装教程详解
2020/08/04 Python
深入浅析pycharm中 Make available to all projects的含义
2020/09/15 Python
移动端html5 meta标签的神奇功效
2016/01/06 HTML / CSS
HTML5去掉输入框type为number时的上下箭头的实现方法
2020/01/03 HTML / CSS
Cotton On美国网站:澳洲时装连锁品牌
2016/10/25 全球购物
探索欧洲最好的品牌:Bombinate
2019/06/14 全球购物
纯净、自信、100%的羊绒服装:360Cashmere
2021/02/20 全球购物
销售人员自我评价怎么写
2013/09/19 职场文书
大学本科毕业生求职信范文
2013/12/18 职场文书
画展邀请函
2015/01/31 职场文书
税务会计岗位职责
2015/04/02 职场文书
2015年售后服务工作总结
2015/04/25 职场文书
2016年大学生就业指导课心得体会
2015/10/09 职场文书
hive数据仓库新增字段方法
2022/06/25 数据库