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生成EXCEL的东东
Oct 09 PHP
php中对xml读取的相关函数的介绍一
Jun 05 PHP
php中的Base62类(适用于数值转字符串)
Aug 12 PHP
php获取字符串中各个字符出现次数的方法
Feb 23 PHP
php使用glob函数遍历文件和目录详解
Sep 23 PHP
php获取远程图片并下载保存到本地的方法分析
Oct 08 PHP
PHP 应用容器化以及部署方法
Feb 12 PHP
thinkPHP框架实现多表查询的方法
Jun 14 PHP
thinkPHP框架通过Redis实现增删改查操作的方法详解
May 13 PHP
PHP检查文件是否存在,不存在自动创建及读取文件内容操作示例
Jan 23 PHP
CI框架简单分页类用法示例
Jun 06 PHP
详解PHP Swoole与TCP三次握手
May 27 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/12/06 PHP
PHP实现服务器状态监控的方法
2014/12/09 PHP
PHP实现对png图像进行缩放的方法(支持透明背景)
2015/07/15 PHP
Linux下快速搭建php开发环境
2017/03/13 PHP
Javascript 复制数组实现代码
2009/11/26 Javascript
JS无限树状列表实现代码
2011/01/11 Javascript
jquery实现每个数字上都带进度条的幻灯片
2013/02/20 Javascript
jQuery 网易相册鼠标移动显示隐藏效果实现代码
2013/03/31 Javascript
常见的原始JS选择器使用方法总结
2014/04/09 Javascript
javascript调试之DOM断点调试法使用技巧分享
2014/04/15 Javascript
当滚动条滚动到页面底部自动加载增加内容的js代码
2014/05/13 Javascript
JS实现转动随机数抽奖特效代码
2020/04/16 Javascript
浅谈JavaScript变量的自动转换和语句
2016/06/12 Javascript
浅谈js函数中的实例对象、类对象、局部变量(局部函数)
2016/11/20 Javascript
使用svg实现动态时钟效果
2018/07/17 Javascript
Vue中消息横向滚动时setInterval清不掉的问题及解决方法
2019/08/23 Javascript
[02:34]DOTA2英雄基础教程 幽鬼
2014/01/02 DOTA
python修改注册表终止360进程实例
2014/10/13 Python
python xml.etree.ElementTree遍历xml所有节点实例详解
2016/12/04 Python
PyQt5 QTable插入图片并动态更新的实例
2019/06/18 Python
python和c语言的主要区别总结
2019/07/07 Python
使用Python调取任意数字资产钱包余额功能
2019/08/15 Python
python中property属性的介绍及其应用详解
2019/08/29 Python
Python获取、格式化当前时间日期的方法
2020/02/10 Python
Python基础教程(一)——Windows搭建开发Python开发环境
2020/07/20 Python
Python生成器传参数及返回值原理解析
2020/07/22 Python
html5 worker 实例(二) 图片变换效果
2013/06/24 HTML / CSS
html5实现移动端适配完美写法
2017/11/16 HTML / CSS
美国婴童服装市场上的领先品牌:Carter’s
2018/02/08 全球购物
Melissa鞋英国官方网站:Nonnon
2019/05/01 全球购物
电气工程及其自动化自我评价四篇
2013/09/24 职场文书
简单的辞职信范文
2014/01/18 职场文书
物流创业计划书
2014/02/01 职场文书
股权投资意向书
2014/04/01 职场文书
个人事迹材料范文
2014/12/29 职场文书
pytorch 实现变分自动编码器的操作
2021/05/24 Python