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设计模式之结构模式的深入解析
Jun 13 PHP
PHP针对常规模板引擎中与CSS/JSON冲突的解决方法
Aug 19 PHP
php查询ip所在地的方法
Dec 05 PHP
smarty内置函数capture用法分析
Jan 22 PHP
php文件缓存类用法实例分析
Apr 22 PHP
PHP简单实现文本计数器的方法
Apr 28 PHP
CI框架数据库查询缓存优化的方法
Nov 21 PHP
php压缩文件夹最新版
Jul 18 PHP
PHP使用PDO创建MySQL数据库、表及插入多条数据操作示例
May 30 PHP
php实现简单的守护进程创建、开启与关闭操作
Aug 13 PHP
php面向对象基础详解【星际争霸游戏案例】
Jan 23 PHP
php设计模式之观察者模式实例详解【星际争霸游戏案例】
Mar 30 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下实现折线图效果的代码
2007/04/28 PHP
php下通过POST还是GET来传值
2008/06/05 PHP
控制PHP的输出:缓存并压缩动态页面
2013/06/11 PHP
ThinkPHP使用心得分享-上传类UploadFile的使用
2014/05/15 PHP
PHP 前加at符合@的作用解析
2015/07/31 PHP
Laravel中注册Facades的步骤详解
2016/03/16 PHP
php flush无效,IIS7下php实时输出的方法
2016/08/25 PHP
PHP实现对图片的反色处理功能【测试可用】
2018/02/01 PHP
js 遍历对象的属性的代码
2011/12/29 Javascript
JS对象与JSON格式数据相互转换
2012/02/20 Javascript
将光标定位于输入框最右侧实现代码
2012/12/04 Javascript
angularJS提交表单(form)
2015/02/09 Javascript
EasyUI实现二级页面的内容勾选的方法
2015/03/01 Javascript
Javascript中的arguments与重载介绍
2015/03/15 Javascript
JavaScript面对国际化编程时的一些建议
2015/06/24 Javascript
jquery判断复选框是否被选中的方法
2015/10/16 Javascript
JS拖动选择table里的单元格完整实例【基于jQuery】
2019/05/28 jQuery
JS获取表格视图所选行号的ids过程解析
2020/02/21 Javascript
JS实现小米轮播图
2020/09/21 Javascript
微信小程序实现简单的select下拉框
2020/11/23 Javascript
10分钟学会js处理json的常用方法
2020/12/06 Javascript
简单介绍Python中利用生成器实现的并发编程
2015/05/04 Python
python读取word文档的方法
2015/05/09 Python
python实现内存监控系统
2021/03/07 Python
用Python实现二叉树、二叉树非递归遍历及绘制的例子
2019/08/09 Python
Python 如何优雅的将数字转化为时间格式的方法
2019/09/26 Python
pycharm解决关闭flask后依旧可以访问服务的问题
2020/04/03 Python
HTML5 canvas基本绘图之绘制线段
2016/06/27 HTML / CSS
Peter Alexander新西兰站:澳大利亚领先的睡衣设计师品牌
2016/12/10 全球购物
梅西酒窖:Macy’s Wine Cellar
2018/01/07 全球购物
英国领先的电子、技术和办公用品购物网站:Ebuyer
2018/04/04 全球购物
.net C#面试题
2012/08/28 面试题
党员创先争优公开承诺书
2014/03/28 职场文书
出纳年终工作总结2014
2014/12/05 职场文书
秦兵马俑导游词
2015/02/02 职场文书
《分一些蚊子进来》读后感3篇
2020/01/09 职场文书