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

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

发现问题

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

解决问题

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

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

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

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

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

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

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

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

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

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

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

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

getSites($type); } echo("end foreach\n");

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

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

$urlKeys[$urlKey] = 1;
$result[] = $site;
}
return $result;

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

总结

该问题从发现到解决花了大概1天时间,虽然最后证明是低级的代码BUG导致,但是整个排查过程还是挺有收获的,最开始的想当然证明是非常肤浅的,过程中tcpdump和strace的结果也已经很能说明问题了,对各个工具的应用应该要更加熟练,工具的结果也要深入分析。以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

原创声明
本站部分文章基于互联网的整理,我们会把真正“有用/优质”的文章整理提供给各位开发者。本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
本文链接:http://www.jiecseo.com/news/show_63311.html