PHP实现通过strace定位故障原因的方法

本文实例讲述了PHP实现通过strace定位故障原因的方法。分享给大家供大家参考,具体如下:

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

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

connect() failed (110: Connection timed out) while connecting to upstream
connect() failed (111: 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) = 1 ]; 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     132    179      brk
 14.71  0.011350     140    81      mlock
 12.70  0.009798     15    658    16 recvfrom
 8.96  0.006910      7    927      read
 6.61  0.005097     43    119      accept
 5.57  0.004294      4    977      poll
 3.13  0.002415      7    359      write
 2.82  0.002177      7    311      sendto
 2.64  0.002033      2   1201     1 stat
 2.27  0.001750      1   2312      gettimeofday
 2.11  0.001626      1   1428      rt_sigaction
 1.55  0.001199      2    730      fstat
 1.29  0.000998     10    100    100 connect
 1.03  0.000792      4    178      shutdown
 1.00  0.000773      2    492      open
 0.93  0.000720      1    711      close
 0.49  0.000381      2    238      chdir
 0.35  0.000271      3    87      select
 0.29  0.000224      1    357      setitimer
 0.21  0.000159      2    81      munlock
 0.17  0.000133      2    88      getsockopt
 0.14  0.000110      1    149      lseek
 0.14  0.000106      1    121      mmap
 0.11  0.000086      1    121      munmap
 0.09  0.000072      0    238      rt_sigprocmask
 0.08  0.000063      4    17      lstat
 0.07  0.000054      0    313      uname
 0.00  0.000000      0    15     1 access
 0.00  0.000000      0    100      socket
 0.00  0.000000      0    101      setsockopt
 0.00  0.000000      0    277      fcntl
------ ----------- ----------- --------- --------- ----------------
100.00  0.077145         13066    118 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) 2>&1 | grep -B 10 brk
stat("/path/to/script.php", {...}) = 0 <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     11    138     2 recvfrom
 16.11  0.001000     33    30      accept
 7.86  0.000488      8    59      sendto
 7.35  0.000456      1    360      rt_sigaction
 6.73  0.000418      2    198      poll
 5.72  0.000355      1    285      stat
 4.54  0.000282      0    573      gettimeofday
 4.41  0.000274      7    42      shutdown
 4.40  0.000273      2    137      open
 3.72  0.000231      1    197      fstat
 2.93  0.000182      1    187      close
 2.56  0.000159      2    90      setitimer
 2.13  0.000132      1    244      read
 1.71  0.000106      4    30      munmap
 1.16  0.000072      1    60      chdir
 1.13  0.000070      4    18      setsockopt
 1.05  0.000065      1    100      write
 1.05  0.000065      1    64      lseek
 0.95  0.000059      1    75      uname
 0.00  0.000000      0    30      mmap
 0.00  0.000000      0    60      rt_sigprocmask
 0.00  0.000000      0     3     2 access
 0.00  0.000000      0     9      select
 0.00  0.000000      0    20      socket
 0.00  0.000000      0    20    20 connect
 0.00  0.000000      0    18      getsockopt
 0.00  0.000000      0    54      fcntl
 0.00  0.000000      0     9      mlock
 0.00  0.000000      0     9      munlock
------ ----------- ----------- --------- --------- ----------------
100.00  0.006208         3119    24 total

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

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

更多关于PHP相关内容感兴趣的读者可查看本站专题:《PHP错误与异常处理方法总结》、《php字符串(string)用法总结》、《PHP数组(Array)操作技巧大全》、《PHP运算与运算符用法总结》、《PHP网络编程技巧总结》、《PHP基本语法入门教程》及《php面向对象程序设计入门教程》

希望本文所述对大家PHP程序设计有所帮助。

您可能感兴趣的文章:

  • php+redis在实际项目中HTTP 500: Internal Server Error故障排除
  • IIS下PHP运行环境搭建与网页服务器故障排查方法
  • IIS PHP环境Temp文件夹的权限问题引起的网站故障
  • 基于PHP7错误处理与异常处理方法(详解)
  • phpStudy配置多站点多域名方法及遇到的403错误解决方法
  • PHP中常见的错误与异常处理总结大全
  • php实现的错误处理封装类实例
  • 浅谈PHP错误类型及屏蔽方法
  • php中的异常和错误浅析
  • PHP中Notice错误常见解决方法
(0)

相关推荐

  • php中的异常和错误浅析

    本文主要介绍了php中的异常和错误,分享给大家供大家参考学习,下面来一起看看详细的介绍: 一.异常与错误 异常是指程序运行中不符合预期情况以及与正常流程不同的状况.错误则属于自身问题,是一种非法语法或者环境问题导致的.让编译器无法通过检查设置无法运行的情况. 由于php最开始是没有异常处理,后来为了进军企业级开发,模仿java等语言,推出了异常.导致php中遇到任何自身错误都会触发一个错误,而不是抛出一个异常(某些情况下,会同时抛出错误和异常).PHP一旦遇到非正常代码,大多数情况下,都是直接抛

  • 基于PHP7错误处理与异常处理方法(详解)

    PHP7错误处理 PHP 7 改变了大多数错误的报告方式.不同于传统(PHP 5)的错误报告机制,现在大多数错误被作为 Error 异常抛出. 这种 Error 异常可以像 Exception 异常一样被第一个匹配的 try / catch 块所捕获.如果没有匹配的 catch 块,则调用异常处理函数(事先通过 set_exception_handler() 注册)进行处理. 如果尚未注册异常处理函数,则按照传统方式处理:被报告为一个致命错误(Fatal Error). Error 类并非继承自

  • IIS下PHP运行环境搭建与网页服务器故障排查方法

    一.对于php页面完全无法访问的情况 1.确认是php的问题还是iis等服务器的问题 判断方法,在目录下放一个静态文件,通过浏览器判断这个静态文件可否访问.若可以访问,即为php问题. 如果是IIS的问题,常见的有两种情况,一个判断的利器是telnet. linux的telnet其实更加好用,因为默认是打开回显的.而windows,则需要用 set localecho,否则看不到输入的文字 1.1 防火墙禁止80端口 进入telnet后,输入指令,open 域名 80 如:open www.ba

  • php+redis在实际项目中HTTP 500: Internal Server Error故障排除

    问题描述 用户量快速增长,访问量在短时间内翻倍,由于前期容量规划做得比较好,硬件资源可以支撑,可是软件系统方面出现了大问题: 40% 的请求都会返回 HTTP 500: Internal Server Error 通过查看日志,发现错误是在 PHP <-> Redis 的连接处理上 调试处理 第1次 刚开始时并没有找到根本原因,只能尝试各种与错误相关的办法,例如: 增加 PHP 连接数,并把超时时间从 500ms 增加到 2.5s 禁止掉 PHP 设置中的 default_socket_tim

  • IIS PHP环境Temp文件夹的权限问题引起的网站故障

    前几天不知为何,服务器抽风严重- 服务器非常慢,把WINDOWS2003重启了,结果连不上服务器.结果悲剧的去了机房. 查找不出什么问题,GHOST恢复系统. 几天后回去又装了个新站上去.再过两天后,问题又出现了. 排查过程 检查网页服务器,病毒,木马?是否被入侵? 检查数据库服务器,CPU,内存,网络一切正常,相应的端口也做了 IP 限制,只允许网页服务器访问,查看日志文件也没有异常情况 检查其余的 .Net 网站,速度正常,没有任何问题 检查其余的 Php 网站,架构方法一样,也没有任何问题

  • PHP中常见的错误与异常处理总结大全

    前言 当我们开发程序时,程序出现问题是很常见的,当出现了异常与错误我们该如何处理呢?本文将详细给大家介绍PHP错误与异常处理的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: 一.PHP错误处理 1.语法错误 2.运行时错误 3.逻辑错误:不提示错误,但功能不对,最麻烦 4.三种级别:notice/warning/fatal error(无法继续执行) 5.错误报告显示: a.可以在php.ini中修改error_reporting项目,以限定错误报告类型,如:error

  • php实现的错误处理封装类实例

    本文实例讲述了php实现的错误处理封装类.分享给大家供大家参考,具体如下: 1.创建MyErrorHandler.php文件 代码如下: <?php class MyErrorHandler { public $message; public $filename; public $line; public $vars = array(); protected $_noticeLog = 'F:\root\noticeLog.log'; public function __construct ($

  • 浅谈PHP错误类型及屏蔽方法

    程序只要在运行,就免不了会出现错误,错误很常见,比如Error,Notice,Warning等等.在PHP中,主要有以下3种错误类型. 1.注意(Notices) 这些都是比较小而且不严重的错误,比如去访问一个未被定义的变量.通常,这类的错误是不提示给用户的,但有时这些错误会影响到运行的结果. 2.警告(Warnings) 这就是稍微严重一些的错误了,比如想要包含include()一个本身不存在的文件.这样的错误信息会提示给用户,但不会导致程序终止运行. 3.致命错误(Fatal errors)

  • PHP中Notice错误常见解决方法

    对于初学者,肯定会遇到不同的错误提示,比如:警告,致命,等等,其中NOTICE错误等级最低,页面中,好多类似 Notice: Use of undefined constant title - assumed 'title' in D:\wamp\www\phpmysqlweb\bbs\list.php on line 17 .虽然不影响正常运行,但是看着很不舒服,对于那些不懂编程的使用者来说,可能心里会很害怕,那么怎么解决呢?究其原因,是因为变量没有定义,下面看看我在编程中经常用到的解决方法吧

  • phpStudy配置多站点多域名方法及遇到的403错误解决方法

    此文不太适合一点不懂多站点多域名的.适合于碰到难处了,一点就透.ok.比较随意. 首先,打开站点域名管理,配置如下,网站域名和目录都可以自己设置,端口重设一个,不要是80了,然后先新增再保存. 打开配置文件vhosts,会看到新增了801端口的东西 在配置文件http.conf中添加 ,在80后添加新端口号 然后在hosts文件中配置下,127.0.0.1 www.aaa.com 再就是,玛德GG了,出现forbidden了,403,没有访问权限. 废了很大劲终于找到了解决方案,下面先复制解决方

随机推荐