PHP脚本只显示一次,记录一次排查PHP脚本执行卡住的问题

记录一次排查PHP脚本执行卡住的问题

这里有新鲜出炉的 PHP 面向对象编程,程序狗速度看过来!

PHP 开源脚本语言

PHP(外文名: Hypertext Preprocessor,中文名:"超文本预处理器")是一种通用开源脚本语言。语法吸收了 C 语言、Java 和 Perl 的特点,入门门槛较低,易于学习,使用广泛,主要适用于 web 开发领域。PHP 的文件后缀名为 php。

最近通过监控发现服务器中的一台机器异常,通过排除发现可能是 php 执行脚本出现了问题,于是通过一步步排查,最终解决了这个问题,下面这篇文章主要是记录了一次排查 PHP 脚本执行卡住的问题, 需要的朋友可以参考下。

发现问题

最近忽然从监控中发现,我们一个服务的一台机器负载比同机房的其他机器要高,而流入流出流量没有差别,进一步查看发现每个机房都有一台机器存在相同的现象,梳理后发现有问题的这些机器相比正常的机器多跑了一些 PHP 脚本,于是猜测是执行脚本出问题导致。

解决问题

登录机器后执行 top 命令,果然发现存在一个 CPU 占用较高的 PHP 进程,然后执行下列命令,发现存在一个由 crontab 启动的执行了很长时间的 PHP 脚本:

ps aux|grep'php'|grep-v'php-fpm'

由于之前也遇到过 PHP 脚本执行卡住的类似情况,当时的怀疑是跨机房的 Mysql 查询在网络抖动时导致 Mysql 连接卡住了,于是理所当然的将所有卡住的进程都 kill 掉了,再从负载上看机器马上就恢复正常了,于是心满意足的跑去干别的了。

过了一段时间,刷了下监控,发现问题又出现了,注释掉 crontab 并 kill 掉进程后,手动执行问题脚本,竟然能稳定复现问题!看来是把问题想得太简单了,尝试用 strace 命令看下卡住的进程当前究竟在干什么:

[tabalt@localhost~]sudo strace-p13793

Process13793attached-interrupt to quit

什么输出都没有!再用 netstat 看下这个进程是否打开了什么端口:

[tabalt@localhost~]sudo netstat-tunpa|grep13793

tcp00192.168.1.100:38019192.168.1.101:3306ESTABLISHED13793/php

tcp00192.168.1.100:47107192.168.1.102:6379CLOSE_WAIT13793/php

可以看到进程打开了两个端口,分别与 Mysql 和 Redis 建立了连接,并且处于连接建立(ESTABLISHED)和对方主动关闭连接(CLOSE_WAIT)的状态;初看确实像是和数据库的连接卡住了,但是因为吃过亏上过当,咱们使用 tcpdump 抓包看进程和数据库之间的交互:

tcpdump-i eth0 host192.168.1.101andport3306-w~/mysql.cap

抓了好一会,~/mysql.cap 文件中却也没有任何输出,难道进程和 Mysql 之间已经没有任何交互了?那为什么连接建立没有关闭呢?看来只能从头追踪一下脚本的执行情况了:

首先为了能来得及 strace 到进程,在 PHP 脚本最开始的时候输出进程的 pid 并 sleep 10s:

echo getmypid();sleep(10);

然后启动 tcpdump 准备抓包本机和 Mysql 的交互过程。

最后执行 PHP 脚本,并复制输出的 pid 后在新窗口中执行 strace 命令。

这下 strace 和 tcpdump 都有内容了!从 strace 结果看 recvfrom 之后不再有 poll,但并没有看出来有什么不对:

//...

poll([{fd=4,events=POLLIN|POLLERR|POLLHUP}],1,1471228928)=1([{fd=4,revents=POLLIN}])

recvfrom(4,"://xxx.com/\0\0\23jiadia"...,271,MSG_DONTWAIT,NULL,NULL)=271

poll([{fd=4,events=POLLIN|POLLERR|POLLHUP}],1,1471228928)=1([{fd=4,revents=POLLIN}])

recvfrom(4,"_b?ie=UTF8&node=658390051\0\0008www."...,271,MSG_DONTWAIT,NULL,NULL)=206

再从抓包结果看,执行了两条 SQL 查询语句之后,进程没有再次发送查询请求的包,从程序记录 SQL 语句日志中,也发现确实只执行了两条:

select*fromsiteswheretype=1limit50;

select*fromsiteswheretype=2limit50;

但从这些现象中,仍然没有能看出任何端倪,只好祭出终极大法:输出调试!大概看了下代码,并在关键地方添加输出语句,于是代码看起来如下:

echo("start foreach\n");

foreach($typesas$type)

{

echo("foreach $type\n");

$result[$type]=$this->getSites($type);

}

echo("end foreach\n");

执行后输出如下,查询 type 为 2 的网址时卡住了:

startforeach

foreach1

foreach2

开始怀疑调用的 getSites() 方法有问题,代码如下:

$sites=array();// 省略从数据库查询的代码

$siteNum=8;// 省略从配置读的代码

$urlKeys=$result=array();

for($i=0;$i

do{

$site=array_shift($sites);

$urlKey=md5($site['url']);

}while(array_key_exists($urlKey,$urlKeys));

$urlKeys[$urlKey]=1;

$result[]=$site;

}

return$result;

原来这里为了实现拿 8 个不重复的网址写了 2 个循环,如果结果中不重复的网址只有 7 个就会有一个空,少于 7 个就会有死循环!于是查了下 type 为 2 的网址个数,果然是只有 6 个!

总结

来源: http://www.phperz.com/article/17/0812/340786.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值