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 相关文章推荐
第十二节--类的自动加载
Nov 16 PHP
PHP开发中常用的8个小技巧
Aug 27 PHP
yii框架中的Url生产问题小结
Jan 16 PHP
php用header函数实现301跳转代码实例
Nov 25 PHP
PHP面向对象程序设计之接口用法
Aug 20 PHP
php实现过滤表单提交中html标签的方法
Oct 17 PHP
PHP中curl_setopt函数用法实例分析
Apr 16 PHP
PHP设计模式之适配器模式代码实例
May 11 PHP
PHP排序算法类实例
Jun 17 PHP
Zend Framework教程之Zend_Db_Table用法详解
Mar 21 PHP
thinkPHP5.0框架独立配置与动态配置方法
Mar 17 PHP
Thinkphp5.0 框架实现控制器向视图view赋值及视图view取值操作示例
Oct 12 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
mysql4.1以上版本连接时出现Client does not support authentication protocol问题解决办法
2007/03/15 PHP
PHPWind 发帖回帖Api PHP版打包下载
2010/02/08 PHP
腾讯QQ php程序员面试题目整理
2010/06/08 PHP
php注册登录系统简化版
2020/12/28 PHP
YII Framework框架教程之国际化实现方法
2016/03/14 PHP
Zend Framework动作控制器用法示例
2016/12/09 PHP
PHP 访问数据库配置通用方法(json)
2018/05/20 PHP
关于图片验证码设计的思考
2007/01/29 Javascript
用JavaScript显示随机图像或引用
2009/04/21 Javascript
JavaScript Event学习第四章 传统的事件注册模型
2010/02/07 Javascript
javascript基础第一章 JavaScript与用户端
2010/07/22 Javascript
javascript设计模式之module(模块)模式
2016/08/19 Javascript
原生js实现无缝轮播图效果
2017/01/11 Javascript
canvas实现动态小球重叠效果
2017/02/06 Javascript
JavaScript中的子窗口与父窗口的互相调用问题
2017/02/08 Javascript
Vue.js中用webpack合并打包多个组件并实现按需加载
2017/02/17 Javascript
mocha的时序规则讲解
2019/02/16 Javascript
vue递归组件实战之简单树形控件实例代码
2019/08/27 Javascript
python中利用await关键字如何等待Future对象完成详解
2017/09/07 Python
python中使用iterrows()对dataframe进行遍历的实例
2018/06/09 Python
Django管理员账号和密码忘记的完美解决方法
2018/12/06 Python
Python3.7 dataclass使用指南小结
2019/02/22 Python
selenium+python环境配置教程详解
2019/05/28 Python
Django实现微信小程序的登录验证功能并维护登录态
2019/07/04 Python
opencv设置采集视频分辨率方式
2019/12/10 Python
如何利用Python matplotlib绘制雷达图
2020/12/21 Python
HTML5调用手机摄像头拍照的实现思路及代码
2014/06/15 HTML / CSS
HTML5利用约束验证API来检查表单的输入数据的代码实例
2016/12/20 HTML / CSS
vivo智能手机官方商城:vivo
2016/09/22 全球购物
个人委托书范文
2015/01/28 职场文书
董事长秘书岗位职责
2015/02/13 职场文书
工厂清洁工岗位职责
2015/02/14 职场文书
个人党性分析总结
2015/03/05 职场文书
《称赞》教学反思
2016/02/17 职场文书
创业计划书之蛋糕店
2019/08/29 职场文书
前端传参数进行Mybatis调用mysql存储过程执行返回值详解
2022/08/14 MySQL