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 相关文章推荐
一个可查询所有表的“通用”查询分页类
Oct 09 PHP
Win2000+Apache+MySql+PHP4+PERL安装使用小结
Oct 09 PHP
粗略计算在线时间,bug:ip相同
Dec 09 PHP
给apache2.2加上mod_encoding模块後 php5.2.0 处理url出现bug
Apr 12 PHP
PHP数组 为文章加关键字连接 文章内容自动加链接
Dec 29 PHP
PHP swfupload图片上传的实例代码
Sep 30 PHP
使用图灵api创建微信聊天机器人
Jul 23 PHP
CI(Codeigniter)的Setting增强配置类实例
Jan 06 PHP
php加密解密字符串示例
Oct 13 PHP
Yaf框架封装的MySQL数据库操作示例
Mar 06 PHP
laravel 之 Eloquent 模型修改器和序列化示例
Oct 17 PHP
PHP 实现 JSON 数据的编码和解码操作详解
Apr 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
Yii2前后台分离及migrate使用(七)
2016/05/04 PHP
PHP批量修改文件名称的方法分析
2017/02/27 PHP
php生成0~1随机小数的方法(必看)
2017/04/05 PHP
php读取本地json文件的实例
2018/03/07 PHP
php中目录操作opendir()、readdir()及scandir()用法示例
2019/06/08 PHP
Javascript this关键字使用分析
2008/10/21 Javascript
javascript 判断数组是否已包含了某个元素的函数
2010/05/30 Javascript
poshytip 基于jquery的 插件 主要用于显示微博人的图像和鼠标提示等
2012/10/12 Javascript
javascript基础知识分享之类与函数化
2016/02/13 Javascript
jQuery实现的购物车物品数量加减功能代码
2016/11/16 Javascript
值得收藏的vuejs安装教程
2017/11/21 Javascript
ReactNative之FlatList的具体使用方法
2017/11/29 Javascript
vue-router源码之history类的浅析
2019/05/21 Javascript
Vue实现点击导航栏当前标签后变色功能
2020/08/19 Javascript
vuex中遇到的坑,vuex数据改变,组件中页面不渲染操作
2020/11/16 Javascript
[53:38]OG vs LGD 2018国际邀请赛淘汰赛BO3 第三场 8.26
2018/08/30 DOTA
Python简单计算给定某一年的某一天是星期几示例
2018/06/27 Python
Python爬虫PyQuery库基本用法入门教程
2018/08/04 Python
Python第三方库face_recognition在windows上的安装过程
2019/05/03 Python
Django中如何使用sass的方法步骤
2019/07/09 Python
深入了解Python iter() 方法的用法
2019/07/11 Python
程序员的七夕用30行代码让Python化身表白神器
2019/08/07 Python
Flask中endpoint的理解(小结)
2019/12/11 Python
python 按钮点击关闭窗口的实现
2020/03/04 Python
Python实现打包成库供别的模块调用
2020/07/13 Python
用python制作个音乐下载器
2021/01/30 Python
利用Bootstrap实现漂亮简洁的CSS3价格表实例源码
2017/03/02 HTML / CSS
Canvas实现保存图片到本地的示例代码
2018/06/28 HTML / CSS
设计总监岗位职责
2013/12/07 职场文书
编辑找工作求职信范文
2013/12/16 职场文书
大学旷课检讨书
2014/01/28 职场文书
竞选部长演讲稿
2014/04/26 职场文书
售房协议书
2014/08/19 职场文书
如何设计高效合理的MySQL查询语句
2021/05/26 MySQL
GTX1660显卡搭配显示器推荐
2022/04/19 数码科技
Win11任务栏无法正常显示 资源管理器不停重启的解决方法
2022/07/07 数码科技