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中使用Oracle数据库(6)
Oct 09 PHP
php中的一个中文字符串截取函数
Feb 14 PHP
PHP开发中常用的三个表单验证函数使用小结
Mar 03 PHP
PHP 页面编码声明方法详解(header或meta)
Mar 12 PHP
PHP操作MongoDB时的整数问题及对策说明
May 02 PHP
学习使用curl采集curl使用方法
Jan 11 PHP
利用php+mcDropdown实现文件路径可在下拉框选择
Aug 07 PHP
php列出mysql表所有行和列的方法
Mar 13 PHP
PHP中trim()函数简单使用指南
Apr 16 PHP
浅析PHP中Session可能会引起并发问题
Jul 23 PHP
php精度计算的问题解析
Jun 21 PHP
PHP批斗大会之缺失的异常详解
Jul 09 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 Stream_*系列函数
2010/08/01 PHP
实例讲解如何在PHP的Yii框架中进行错误和异常处理
2016/03/17 PHP
ThinkPHP开发--使用七牛云储存
2017/09/14 PHP
Laravel 实现密码重置功能
2018/02/23 PHP
IE8 原生JSON支持
2009/04/13 Javascript
extjs grid设置某列背景颜色和字体颜色的实现方法
2010/09/06 Javascript
jquery常用方法及使用示例汇总
2014/11/08 Javascript
JavaScript实现图片轮播的方法
2015/07/31 Javascript
Javascript 字符串模板的简单实现
2016/02/13 Javascript
学习vue.js中class与style绑定
2016/12/03 Javascript
javaScript嗅探执行神器-sniffer.js
2017/02/14 Javascript
bootstrap table表格客户端分页实例
2017/08/07 Javascript
vue中添加mp3音频文件的方法
2018/03/02 Javascript
基于JS判断对象是否是数组
2020/01/10 Javascript
js+css实现全屏侧边栏
2020/06/16 Javascript
在vue中实现禁止屏幕滚动,禁止屏幕滑动
2020/07/22 Javascript
使用Python获取CPU、内存和硬盘等windowns系统信息的2个例子
2014/04/15 Python
Python使用剪切板的方法
2017/06/06 Python
用Python中的turtle模块画图两只小羊方法
2019/04/09 Python
Python谱减法语音降噪实例
2019/12/18 Python
python pandas dataframe 去重函数的具体使用
2020/07/20 Python
Python全局变量与global关键字常见错误解决方案
2020/10/05 Python
python利用opencv实现颜色检测
2021/02/23 Python
全面总结使用CSS实现水平垂直居中效果的方法
2016/03/10 HTML / CSS
生物医学工程专业学生求职信范文分享
2013/12/14 职场文书
销售人员求职的自我评价分享
2014/03/15 职场文书
大专毕业生求职信
2014/07/05 职场文书
2014会计年终工作总结
2014/12/20 职场文书
行政人事主管岗位职责
2015/04/11 职场文书
开学第一天的感想
2015/08/10 职场文书
2016年春季趣味运动会开幕词
2016/03/04 职场文书
浅谈如何写好演讲稿?
2019/06/12 职场文书
2019通用版导游词范本!
2019/08/07 职场文书
解决vue $http的get和post请求跨域问题
2021/06/07 Vue.js
mybatis3中@SelectProvider传递参数方式
2021/08/04 Java/Android
Windows 64位 安装 mysql 8.0.28 图文教程
2022/04/19 MySQL