您的位置:首页 > 运维架构 > Linux

php-cgi进程占用cpu资源过大原因分析及解决(找出有问题的linux进程)

2013-06-04 16:12 781 查看
服务器环境:redhat linux 5.5 , nginx , phpfastcgi

在此环境下,一般php-cgi运行是非常稳定的,但也遇到过php-cgi占用太多cpu资源而导致服务器响应过慢,我所遇到的php-cgi进程占用cpu资源过多的原因有:

1. 一些php的扩展与php版本兼容存在问题,实践证明 eAccelerater与某些php版本兼容存在问题,具体表现时启动php-cgi进程后,运行10多分钟,奇慢无比,但静态资源访问很快,服务器负载也很正常(说明nginx没有问题,而是php-cgi进程的问题),解决办法就是从php.ini中禁止掉eAccelerater模块,再重启php-cgi进程即可

2. 程序中可能存在死循环,导致服务器负载超高(使用top指令查看负载高达100+), 需要借助Linux的proc虚拟文件系统找到具体的问题程序

3. php程序不合理使用session , 这个发生在开源微博记事狗程序上,具体表现是有少量php-cgi进程(不超过10个)的cpu使用率达98%以上, 服务器负载在4-8之间,这个问题的解决,仍然需要借助Linux的proc文件系统找出原因。

4. 程序中存在过度耗时且不可能完成的操作(还是程序的问题),例如discuz x 1.5的附件下载功能: source/module/forum/forum_attachement.php中的定义

function getremotefile($file) {

global $_G;

@set_time_limit(0);

if(!@readfile($_G['setting']['ftp']['attachurl'].'forum/'.$file)) {

$ftp = ftpcmd('object');

$tmpfile = @tempnam($_G['setting']['attachdir'], '');

if($ftp->ftp_get($tmpfile, 'forum/'.$file, FTP_BINARY)) {

@readfile($tmpfile);

@unlink($tmpfile);

} else {

@unlink($tmpfile);

return FALSE;

}

}

return TRUE;

}

没有对传入的参数作任何初步检查,而且设置了永不超时,并且使用readfile一次读取超大文件,就可能存在以下问题:

A. 以http方式读取远程附件过度耗时

B. FTP无法连接时,如何及时反馈出错误?

C. readfile是一次性读取文件加载到内存中并输出,当文件过大时,内存消耗惊人

根据实验发现采用readfile一次性读取,内存消耗会明显增加,但是CPU的利用率会下降较多。如果采用分段读取的方式,内存消耗会稍微下降,而CPU占用却会明显上升。

对discuz x 1.5的这个bug较好解决方法就是后台重新正确设置远程附件参数。

以下是我逐步整理的故障排除步骤:

1. 得到占用cpu资源过多的php-cgi进程的pid(进程id), 使用top命令即可,如下图:



经过上图,我们发现,有两个php-cgi进程的cpu资源占用率过高,pid分别是10059,11570,这一般都是程序优化不够造成,如何定位问题的php程序位置?

2. 找出进程所使用的文件

/proc/文件系统保存在内存中,主要保存系统的状态,关键配置等等,而/proc/目录下有很多数字目录,就是进程的相关信息,如下图,我们看看进程10059正在使用哪些文件?



显然,使用了/home/tmp/sess_*文件,这明显是PHP的session文件, 我们查看这个session文件的内容为:view_time|123333312412

到这里,我们已经可以怀疑是由于php程序写入一个叫view_time的session项而引起, 那么剩余的事件就是检查包含view_time的所有php文件,然后修改之(比如改用COOKIE),这实话, 这个view_time并非敏感数据,仅仅记录用户最后访问时间,实在没必要使用代价巨大的session, 而应该使用cookie。

3. 找出有问题的程序,修改之

使用vi编辑以下shell程序(假设网站程序位于/www目录下)

#!/bin/bash

find /www/ -name "*.php" > list.txt

f=`cat ./list.txt`

for n in $f

do

r=`egrep 'view_time' $n`

if [ ! "$r" = "" ] ; then

echo $n

fi

done

运行这个shell程序,将输出包含有view_time的文件, 对记事狗微博系统,产生的问题位于modules/topic.mod.class文件中

以下是miltonzhong我本人解决的一个过程,供以后参照

我和老大的谈话:

27927 fuyi 19 0 319m 15m 8616 R 67.0 0.2 0:40.16 php-cgi

27956 fuyi 25 0 320m 15m 8444 S 64.6 0.2 0:25.34 php-cgi

27946 fuyi 25 0 319m 15m 8532 S 57.6 0.2 0:28.13 php-cgi

27929 fuyi 25 0 319m 14m 8156 R 51.0 0.2 0:19.43 php-cgi

27940 fuyi 16 0 319m 15m 8372 S 43.5 0.2 0:28.09 php-cgi

27926 fuyi 18 0 320m 15m 8432 S 38.0 0.2 0:24.60 php-cgi

27922 fuyi 16 0 320m 16m 9200 S 32.4 0.2 0:24.65 php-cgi

27938 fuyi 16 0 319m 15m 8696 R 19.8 0.2 0:42.01 php-cgi

思考:
可以从哪些地方考虑 2013-06-04 15:30:27
可能是远程获取数据超时导致 2013-06-04 15:30:38
也可能是死循环 2013-06-04 15:30:40
看连接数和是否超时 2013-06-04 15:31:24
我的mysqld和web不是在同一台机器的 2013-06-04 15:31:31
哦。好 2013-06-04 15:33:41
那应该是经常超时了 2013-06-04 15:35:20
有时候同一台服务器放全部服务还快一点感觉 2013-06-04 15:35:52
查询不多肯定是一台快 2013-06-04 15:36:10
如果是内网也可以分开

经过谈话,有所感想,于是试着解决

[root@fuyi02 olwoo]# ps -aux | grep php

Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.7/FAQ

root 2150 0.0 0.0 325276 5584 ? Ss 16:26 0:00 /usr/local/webserver/php/bin/php-cgi --fpm --fpm-config /usr/local/webserver/php/etc/php-fpm.conf

fuyi 2151 9.9 0.1 327864 15940 ? S 16:26 1:05 /usr/local/webserver/php/bin/php-cgi --fpm --fpm-config /usr/local/webserver/php/etc/php-fpm.conf

fuyi 2152 9.3 0.1 327612 15700 ? S 16:26 1:01 /usr/local/webserver/php/bin/php-cgi --fpm --fpm-config /usr/local/webserver/php/etc/php-fpm.conf

fuyi 2153 10.8 0.2 327612 16804 ? S 16:26 1:11 /usr/local/webserver/php/bin/php-cgi --fpm --fpm-config /usr/local/webserver/php/etc/php-fpm.conf

fuyi 2154 9.7 0.1 327864 15460 ? S 16:26 1:03 /usr/local/webserver/php/bin/php-cgi --fpm --fpm-config /usr/local/webserver/php/etc/php-fpm.conf

strace命令跟踪下看看这个进程在做什么东西

strace -p 2185

出现大量

poll([{fd=4, events=POLLIN}], 1, 0) = 0 (Timeout)

select(5, [4], [4], [], {15, 0}) = 1 (out [4], left {15, 0})

poll([{fd=4, events=POLLIN}], 1, 0) = 0 (Timeout)

select(5, [4], [4], [], {15, 0}) = 1 (out [4], left {15, 0})

poll([{fd=4, events=POLLIN}], 1, 0) = 0 (Timeout)

可以看出,这个进程不断的超时,到底为何会超时呢???看来需要从php-cgi的日志中查找问题了,由于原来php-fpm.conf配置的超时时间为0,也就是不设置超时时间。于是先将php-fpm.conf的超时时间设置成5s,然后超过5s的php-cgi的请求就会记录到php的慢日志中

慢日志设置:

<value name="request_slowlog_timeout">3s</value>
<value name="slowlog">logs/slow.log</value>

设置完成,利用命令/usr/local/php/sbin/php-fpm restart重启php-fpm,过一会查看slow.log的内容发现很多如下内容:

script_filename = /data/htdocs/bbs.hrloo.com/apl.php

[0x00007fffb060fd70] file_get_contents() /data/htdocs/bbs.hrloo.com/apl.php:10

超时时间:

<value name="request_terminate_timeout">0s</value>

这个参数的默认值为0秒,意为永不超时



根据以上,解决方法:

ulimit -SHn 102400 (友新解决的)

首页一定要做成伪静态 ,或者缓存

最终解决方法:PHP函数问题导致CPU飙升



strace -p 2185

出现大量

poll([{fd=4, events=POLLIN}], 1, 0) = 0 (Timeout)

select(5, [4], [4], [], {15, 0}) = 1 (out [4], left {15, 0})

poll([{fd=4, events=POLLIN}], 1, 0) = 0 (Timeout)

select(5, [4], [4], [], {15, 0}) = 1 (out [4], left {15, 0})

poll([{fd=4, events=POLLIN}], 1, 0) = 0 (Timeout)
可以看出是file_get_contents()导致的问题



函数解释:就是获取文件内容或者网址内容,获取不到,就一直卡死了,导致耗费资源,这个是ecmall的核心文件。首页调用了。

工具: lsof 也是强大的调试工具

如果是mysql或其他服务导致,可以查看错误日志
如mysql
tail /usr/local/mysql/err_log/mysqlerr.log -f

[ERROR] /usr/local/mysql/libexec/mysqld: Sort aborted





或者内存不够,可以用以下方法迅速找出占用内存大的进程:

ps -e -o "%C : %p : %z : %a"|sort -k5 -nr

ps -e -o'user,uid,pid,comm,args,pcpu,rsz,vsz,stime'|sort -nrk7|head -1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: