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 相关文章推荐
SWFUpload与CI不能正确上传识别文件MIME类型解决方法分享
Apr 18 PHP
PHP安全配置详细说明
Sep 26 PHP
PHP三元运算符的结合性介绍
Jan 10 PHP
PHP高级对象构建 工厂模式的使用
Feb 05 PHP
PHP基础知识回顾
Aug 16 PHP
具有时效性的php加密解密函数代码
Jun 19 PHP
PHP实现获取域名的方法小结
Nov 05 PHP
php序列化函数serialize() 和 unserialize() 与原生函数对比
May 08 PHP
twig模板获取全局变量的方法
Feb 05 PHP
PHP未登录自动跳转到登录页面
Dec 21 PHP
yii2.0整合阿里云oss上传单个文件的示例
Sep 19 PHP
PHP实现微信退款的方法示例
Mar 26 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中养成7个面向对象的好习惯
2010/01/28 PHP
php checkbox复选框值的获取与checkbox默认值输出方法
2010/05/15 PHP
为PHP初学者的8点有效建议
2010/11/20 PHP
php文件缓存类用法实例分析
2015/04/22 PHP
php自动给网址加上链接的方法
2015/06/02 PHP
Laravel使用Caching缓存数据减轻数据库查询压力的方法
2016/03/15 PHP
PHP数组操作实例分析【添加,删除,计算,反转,排序,查找等】
2016/12/24 PHP
js将字符串转成正则表达式的实现方法
2013/11/13 Javascript
Jquery实现的一种常用高亮效果示例代码
2014/01/28 Javascript
jqeury-easyui-layout问题解决方法
2014/03/24 Javascript
Node.js安装教程和NPM包管理器使用详解
2014/08/16 Javascript
基于jquery固定于顶部的导航响应浏览器滚动条事件
2014/11/02 Javascript
jQuery获得指定元素坐标的方法
2015/04/14 Javascript
JS+CSS实现精美的二级导航效果代码
2015/09/17 Javascript
js判断浏览器是否支持严格模式的方法
2016/10/04 Javascript
JavaScript实现随机数生成器(去重)
2017/10/13 Javascript
Vue父子模版传值及组件传值的三种方法
2017/11/27 Javascript
vscode中Vue别名路径提示的实现
2020/07/31 Javascript
[01:17]炒鸡美酒第四天TA暴走
2018/06/05 DOTA
在Python的Bottle框架中使用微信API的示例
2015/04/23 Python
python删除过期log文件操作实例解析
2018/01/31 Python
python全栈知识点总结
2019/07/01 Python
基于python 微信小程序之获取已存在模板消息列表
2019/08/05 Python
Python通过cv2读取多个USB摄像头
2019/08/28 Python
Python制作词云图代码实例
2019/09/09 Python
使用PyTorch实现MNIST手写体识别代码
2020/01/18 Python
pyx文件 生成pyd 文件用于 cython调用的实现
2021/03/04 Python
加拿大领先的优质厨具产品在线购物网站:Golda’s Kitchen
2017/11/17 全球购物
英国领先的电动可调床制造商:Laybrook
2019/12/26 全球购物
写出SQL四条最基本的数据操作语句(DML)
2012/12/12 面试题
计算机毕业大学生推荐信
2013/12/01 职场文书
ktv周年庆活动方案
2014/08/18 职场文书
乡镇干部个人对照检查材料(群众路线)
2014/09/26 职场文书
个人存款证明书
2014/10/18 职场文书
2014年检验科工作总结
2014/11/22 职场文书
罚款通知怎么写
2015/04/22 职场文书