俗话说:不怕贼偷,就怕贼惦记着。在面对故障的时候,我也有类似的感觉:不怕出故障,就怕你不知道故障的原因,故障却隔三差五的找上门来。

十一长假还没结束,服务器却频现高负载,Nginx出现错误日志:

    connect() failed (: Connection timed out) while connecting to upstream
connect() failed (: Connection refused) while connecting to upstream

看上去是Upstream出了问题,在本例中Upstream就是PHP(版本:5.2.5)。可惜监控不完善,我搞不清楚到底是哪出了问题,无奈之下只好不断重启PHP来缓解故障。

如果每次都手动重启服务无疑是个苦差事,幸运的是可以通过CRON设置每分钟执行:

#/bin/bash

LOAD=$(awk '{print $1}' /proc/loadavg)

if [ $(echo "$LOAD > 100" | bc) =  ]; then
/etc/init.d/php-fpm restart
fi

可惜这只是一个权宜之计,要想彻底解决就必须找出故障的真正原因是什么。

闲言碎语不要讲,轮到Strace出场了,统计一下各个系统调用的耗时情况:

shell> strace -c -p $(pgrep -n php-cgi)
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
30.53 0.023554 brk
14.71 0.011350 mlock
12.70 0.009798 recvfrom
8.96 0.006910 read
6.61 0.005097 accept
5.57 0.004294 poll
3.13 0.002415 write
2.82 0.002177 sendto
2.64 0.002033 stat
2.27 0.001750 gettimeofday
2.11 0.001626 rt_sigaction
1.55 0.001199 fstat
1.29 0.000998 connect
1.03 0.000792 shutdown
1.00 0.000773 open
0.93 0.000720 close
0.49 0.000381 chdir
0.35 0.000271 select
0.29 0.000224 setitimer
0.21 0.000159 munlock
0.17 0.000133 getsockopt
0.14 0.000110 lseek
0.14 0.000106 mmap
0.11 0.000086 munmap
0.09 0.000072 rt_sigprocmask
0.08 0.000063 lstat
0.07 0.000054 uname
0.00 0.000000 access
0.00 0.000000 socket
0.00 0.000000 setsockopt
0.00 0.000000 fcntl
------ ----------- ----------- --------- --------- ----------------
100.00 0.077145 total

看上去「brk」非常可疑,它竟然耗费了三成的时间,保险起见,单独确认一下:

shell> strace -T -e brk -p $(pgrep -n php-cgi)
brk(0x1f18000) = 0x1f18000 <0.024025>
brk(0x1f58000) = 0x1f58000 <0.015503>
brk(0x1f98000) = 0x1f98000 <0.013037>
brk(0x1fd8000) = 0x1fd8000 <0.000056>
brk(0x2018000) = 0x2018000 <0.012635>

说明:在Strace中和操作花费时间相关的选项有两个,分别是「-r」和「-T」,它们的差别是「-r」表示相对时间,而「-T」表示绝对时间。 简单统计可以用「-r」,但是需要注意的是在多任务背景下,CPU随时可能会被切换出去做别的事情,所以相对时间不一定准确,此时最好使用「-T」,在行 尾可以看到操作时间,可以发现确实很慢。

在继续定位故障原因前,我们先通过「man brk」来查询一下它的含义:

brk() sets the end of the data segment to the value specified by end_data_segment, when that value is reasonable, the system does have enough memory and the process does not exceed its max data size (see setrlimit(2)).

简单点说就是内存不够用时通过它来申请新内存(data segment),可是为什么呢?

shell> strace -T -p $(pgrep -n php-cgi) >& | grep -B  brk
stat("/path/to/script.php", {...}) = <0.000064>
brk(0x1d9a000) = 0x1d9a000 <0.000067>
brk(0x1dda000) = 0x1dda000 <0.001134>
brk(0x1e1a000) = 0x1e1a000 <0.000065>
brk(0x1e5a000) = 0x1e5a000 <0.012396>
brk(0x1e9a000) = 0x1e9a000 <0.000092>

通过「grep」我们很方便就能获取相关的上下文,反复运行几次,发现每当请求某些PHP脚本时,就会出现若干条耗时的「brk」,而且这些PHP 脚本有一个共同的特点,就是非常大,甚至有几百K,为何会出现这么大的PHP脚本?实际上是程序员为了避免数据库操作,把非常庞大的数组变量通过「var_export」持久化到PHP文件中,然后在程序中通过「include」来获取相应的变量,因为变量太大,所以PHP不得不频繁执行「brk」,不幸的是在本例的环境中,此操作比较慢,从而导致处理请求的时间过长,加之PHP进程数有限,于是乎在Nginx上造成请求拥堵,最终导致高负载故障。

下面需要验证一下推断似乎否正确,首先查询一下有哪些地方涉及问题脚本:

shell> find /path -name "*.php" | xargs grep "script.php"

直接把它们都禁用了,看看服务器是否能缓过来,或许大家觉得这太鲁蒙了,但是特殊情况必须做出特殊的决定,不能像个娘们儿似的优柔寡断,没过多久,服务器负载恢复正常,接着再统计一下系统调用的耗时:

shell> strace -c -p $(pgrep -n php-cgi)
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
24.50 0.001521 recvfrom
16.11 0.001000 accept
7.86 0.000488 sendto
7.35 0.000456 rt_sigaction
6.73 0.000418 poll
5.72 0.000355 stat
4.54 0.000282 gettimeofday
4.41 0.000274 shutdown
4.40 0.000273 open
3.72 0.000231 fstat
2.93 0.000182 close
2.56 0.000159 setitimer
2.13 0.000132 read
1.71 0.000106 munmap
1.16 0.000072 chdir
1.13 0.000070 setsockopt
1.05 0.000065 write
1.05 0.000065 lseek
0.95 0.000059 uname
0.00 0.000000 mmap
0.00 0.000000 rt_sigprocmask
0.00 0.000000 access
0.00 0.000000 select
0.00 0.000000 socket
0.00 0.000000 connect
0.00 0.000000 getsockopt
0.00 0.000000 fcntl
0.00 0.000000 mlock
0.00 0.000000 munlock
------ ----------- ----------- --------- --------- ----------------
100.00 0.006208 total

显而易见,「brk」已经不见了,取而代之的是「recvfrom」和「accept」,不过这些操作本来就是很耗时的,所以可以定位「brk」就是故障的原因。

拥抱故障,每一次故障都是历练。正所谓:天将降大任于斯人也,必先苦其心志,劳其筋骨,饿其体肤,空乏其身,行拂乱其所为,所以动心忍性,增益其所不能。

PHP-通过strace定位故障原因的更多相关文章

  1. 使用strace工具故障排查的5种简单方法

    使用strace工具故障排查的5种简单方法 本文源自5 simple ways to troubleshoot using strace strace 是一个非常简单的工具,用来跟踪可执行程序的系统调 ...

  2. mips64高精度时钟引起ktime_get时间不准,导致饿狗故障原因分析【转】

    转自:http://blog.csdn.net/chenyu105/article/details/7720162 重点关注关中断的情况.临时做了一个版本,在CPU 0上监控所有非0 CPU的时钟中断 ...

  3. 用strace排查故障的5种简单方法(每日一译)

    原文链接:5 simple ways to troubleshoot using Strace 我很意外大部分人都不知道如何使用strace.strace一直是我的首选debug工具,因为它非常的有效 ...

  4. selenium(6):通过多种定位方式还是不能成功定位的原因

    场景:在成功修改密码后,会弹出一个修改成功的提示.通过id.xpath.class.css方式定位后,执行到这一步时候,就会出现错误. 原因:仔细检查了下代码,发现在提交修改的操作到修改成功的提示之间 ...

  5. Wpa_supplicant 调试故障原因分析

    背景 在使用Wpa_supplicant 工具调试Linux的wifi的时候,发现有一些问题.特此记录一下.有些问题是遇到的并已经有了解决方法,一些问题比较发杂,只能作为思路. 问题以及解决办法 1. ...

  6. linux Apache rotatelogs 故障原因及解决方案未生效

    rotatelogs 截断日志.构造.但保存vhost.conf 之后.serverhttpd -k restart 还是无法成功重新启动. 日志文件: (2)No such file or dire ...

  7. K8S Pod Pending 故障原因及解决方案

    文章转载自:https://mp.weixin.qq.com/s/SBpnxLfMq4Ubsvg5WH89lA

  8. HBase工程师线上工作经验总结----HBase常见问题及分析

    阅读本文可以带着下面问题:1.HBase遇到问题,可以从几方面解决问题?2.HBase个别请求为什么很慢?你认为是什么原因?3.客户端读写请求为什么大量出错?该从哪方面来分析?4.大量服务端excep ...

  9. (转)HBase工程师线上工作经验总结----HBase常见问题及分析

    阅读本文可以带着下面问题:1.HBase遇到问题,可以从几方面解决问题?2.HBase个别请求为什么很慢?你认为是什么原因?3.客户端读写请求为什么大量出错?该从哪方面来分析?4.大量服务端excep ...

随机推荐

  1. (转)tomcat与地址栏图标之研究(多浏览器)

    原文:http://hi.baidu.com/hebo_thu/item/fc8c81bb164f5cee4fc7fd90 tomcat与地址栏图标之研究(多浏览器) 最近在做一个java网络应用程序 ...

  2. Ant、Maven、Gradle

    android Gradle project http://www.ibm.com/developerworks/cn/opensource/os-cn-gradle/ http://www.voge ...

  3. FR #3题解

    A. 傻逼题?...前缀和什么的随便搞搞就好了. #include<iostream> #include<cstdio> #include<cstring> #in ...

  4. HDU 5092

    http://acm.hdu.edu.cn/showproblem.php?pid=5092 卡读题,实质是每行取一个点,从上到下找一条路径权值和最小,点可以到达的地方是周围八个格子 类似数塔的dp, ...

  5. Disable Portrait in app

    I had this problem as well as I wanted to constrain my game to only landscape mode. I put this in my ...

  6. 实现Magento多文件上传代码功能开发

    在Magento中上传单个文件很简单,可以直接在继承的Mage_Adminhtml_Block_Widget_Form类中直接添加如下组件Field:  对于图片:   $fieldset->a ...

  7. Carthage

    Carthage Carthage - 一个简单.去集中化的Cocoa依赖管理器

  8. 使用u32过滤器设置基于mac地址的下载限制

    u32过滤器一般使用ip地址作为匹配规则,但按照其定义,它可以匹配ip包头的任意地址,这里使用mac地址限制局域网的下载速度,避免客户端修改ip后其下载速度得不到控制.tc qdisc del dev ...

  9. UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9 in position 0: ordinal not in range(128) 解决办法

    最近在用Python处理中文字符串时,报出了如下错误: UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9 in position 0: ...

  10. C语言redirection

    1)把执行文件xxx.exe 和读取的文件yyy.txt放在一个地方 2)用dos 命令 dir 和cd,打开存放文件的文件夹 3)window 7 情况下 输入xxx.exe < yyy.tx ...