转自 http://www.xiaoxiaoliang.com/?p=235
一,开启日志记录,为以后排查做准备
1.1 开启php-fpm.conf的错误日志和慢执行日志和常规日志, 采样一个小时,就可以根据这些日志的内容进行分析问题
error_log = /tmp/error.log //错误日志
access.log = /tmp/access.$pool.log //常规日志,记录每次访问时间,记录不同参数,以防止恶意攻击,后面会详细解析
access.format = “%R – %u %t \”%m %r%Q%q\” %s %f %{mili}d %{kilo}M %{system}C%%”
slowlog = /tmp/slow.$pool.log //满查询日志记录
request_slowlog_timeout = 3s //慢执行超时时间,可以设置为1秒
1.2 慢查询日志会以堆栈的形式打印每个脚本执行过程中耗时较长的地方,格式如下(可以详细查看我以前发表的PHP慢日志的分析)
[日期时间] [pool www] pid 进程号
script_filename = 脚本文件
[0x00007f2d286c2790] replace() /xxx/Plugin.php:72
[0x00007fff78ab00f0] replace() unknown:0
[0x00007f2d286c2420] call_user_func_array() /xxx/Plugin.php:489
[0x00007fff78ab0430] __call() unknown:0
[0x00007f2d286c1f78] contentEx() /xxx/Abstract/Contents.php:141
[0x00007f2d286c1b78] ___content() /xxx/Widget.php:385
[0x00007f2d286c10f8] +++ dump failed
1.3 使用ll /proc/进程号/fd/ 查看,目前PHP进程在操作什么文件,
使用strace -o /tmp/output.txt -T -tt -F -e trace=all -p <进程号> 来跟踪进程(可查看我以前发表的strace命令的使用)
[root@localhost www]# cat /tmp/output.txt
61905 18:00:40.169437 epoll_wait(8, {}, 1, 409) = 0 <0.410078>
61905 18:00:40.579997 getsockopt(7, SOL_TCP, TCP_INFO, “\n\0\0\0\0\0\0\0@B\17\0\0\0\0\0\30\2\0\0\0\0\0\0\0\0\0\0\200\0\0\0″…, [104]) = 0 <0.000327>
61905 18:00:40.580786 epoll_wait(8, {}, 1, 1000) = 0 <1.001902>
61905 18:00:41.583271 getsockopt(7, SOL_TCP, TCP_INFO, “\n\0\0\0\0\0\0\0@B\17\0\0\0\0\0\30\2\0\0\0\0\0\0\0\0\0\0\200\0\0\0″…, [104]) = 0 <0.000361>
61905 18:00:41.585020 epoll_wait(8, {}, 1, 999) = 0 <1.001523>
61905 18:00:42.587037 getsockopt(7, SOL_TCP, TCP_INFO, “\n\0\0\0\0\0\0\0@B\17\0\0\0\0\0\30\2\0\0\0\0\0\0\0\0\0\0\200\0\0\0″…, [104]) = 0 <0.000408>
二,结合上述的日志,分析可能出现的问题
2.1 php错误日志中,存在大量的常规错误,例如mysql使用了事务导致死锁
2.2 php慢查询日志可以知道那些函数比较耗时,可以考虑使用扩展来实现解决耗时问题, 也有可能是代码中大量使用了file_get_contents等默认不会超时的函数
*file_get_contents一步就做完了打开,读取,关闭的三个动作,过程相当自动化,并且可以读取远程内容,非常方便,在网络状况差的情况下,可能会导致程序执行陷入停滞或者过慢,
因为不停的重试和等待PHP进程本身的超时才会退出。详情可以查看我以前发表过的关于file_get_contents(扩展函数)的内部实现分析的内容
*这个问题还可以根据跟踪PHP-FPM进程知道,如果存在大量的epoll或select超时
2.3 查询TCP连接是否出现大量TIME_WAIT,查询具体原因(可以参考我以前发表的关于TCP的内容和HTTP Keep-Alive在异常情况下推TIME_WAIT的影响)HTTP协议1.1版规定default行为是Keep-Alive,
就是会重用TCP连接传输多个请求/响应, 还有就是从nginx到php-fpm的原地址都是一样,TCP连接就会受限制,因为频繁的TCP连接建立和关闭,会在服务器上留下TIME_WAIT状态,
端口一旦进入服务器的TIME_WAIT黑名单,就会产生相当大的超时于等待,甚至阻塞等待
*详细可以查询TIME_WAIT对http的影响一文
三,排查攻击问题
*php是人类的一个伟大的东西,但不代表他永远不会有bugs, 想了解可以去bugs.php.net查看
3.1 根据常规日志,分析是否存在恶意请求, 例如超大表达的哈希攻击,我们查看PHP的数组的实现原理可以知道,它是一个hash表, 包括$_POST,$_GET, 哈希表其实就是一个数组,每个元素是一个链表,
当存在一个恶意的post,post大量的a[]=1&a[]=1&a[]=1&a[]=1&…这种字段,会导致哈希碰撞产生一个超大链表,从而导致效率严重下降
*详情可以参考我以前发表的关于PHP哈希表碰撞攻击的内容
3.2 php-5的PHP解析multipart/form-datahttp请求的body part请求头时,重复拷贝字符串导致DOS。远程攻击者通过发送恶意构造的multipart/form-data请求,导致服务器CPU资源被耗尽,从而远程DOS服务器。
*此bugs比较新,是今年年中的bugs,估计还存在大量的服务器并没有安装补丁(可以参考我之前发表的PHP5的严重bug, 其实就是一个没有换行符导致的惨剧)
3.3 如果有关注PHP发展的,会知道很多很多这类型的, 人无完人,要根据具体的日志和问题,有可能还能发现并提出新的bug给PHP呢
四,系统优化方面
4.1 不是进程开得越多越好; 拿着16核的CPU, 开着100多个PHP-FPM进程+100多个nginx进程, 纯属装逼; cpu在切换进程的时候不用力吗?
4.2 如果代码和系统的优化都达到最佳状态;可以考虑使用xcache或者apc等进行opcode优化,从而不需要每次都去解析PHP脚本,直接缓存中间代码,可以提升数十倍的性能