一般的排查方法
检查error_log
检查access_log
strace查看系统调用
tcpdump分析网络状况
根据不同问题,要选择合适的方法,最简单的方法还是查看error_log,有些怪异的问题,从error_log中能找到蛛丝马迹。慢请求相关的问题,可以通过access_log记录请求时间。如果log中没有什么可用信息,可以尝试分析一下系统调用或者网络本身。程序本身的问题,可能需要gdb调试。
error_log排查
error_log提供了异常丰富的信息,比如nginx处理请求出错,网络连接出错,后端返回出错,系统调用出错等错误信息,如果是线下调试,则可以开启debug信息,便于调试排查。
connect,write,read timeout等网络超时错误
Permission denied,File not found等系统调用错误
HTTP状态码400,499,500等对应的错误
案例:addops反应了一个很怪异的问题,更新nginx后,http请求不能返回完整的数据,返回部分数据后请求就结束了,每次都能复现。
参看error_log
access_log排查
access_log提供了客户端访问日子,nginx的access_log可灵活定义日志格式,日志内容,比如后端相应时间,后端返回码,后端的failover过程等,另外,也可以对access_log进行统计,比如根据接口统计,根据客户端IP统计,根据服务端IP统计,根据后端响应时间统计。
统计HTTP状态码比例,从而知道nginx服务状况
统计响应时间,判断超时请求
根据经验,如果响应时间分布集中在某个数字,并且标准差很小,则很可能是因为超时。
统计QPS,对比负载是否均衡
案例:一次nginx灰度过程中,我们选取一台nginx上线,在接下来的半小时内,我们发现nginx占用内存不断增大,直到耗尽系统资源。
排查
Error_log中没有发现错误信息
监控发现新上线nginx流量大增
分析access_log,发现新上线nginx的QPS是原机器的6-8倍
KEEPalive导致流量负债不均衡
LVS作为负载均衡Server时,后端nginx设置的keepalive_timeout时间不能差异过大
LVS负载均衡示意图
LVS是TCP层面的负载均衡,只能做到连接的均衡
连接保持的时间越长,导致路由到这个连接额请求越多
案例:Nginx反向代理,有时发现某些接口较慢,通常在3s左右
Nginx access_log可以记录 upstream_resonse_time
分析access_log的upstream_response_time,发现响应大约3s多
分析后端access_log,处理时间在毫秒级别
最终发现,问题是由于nginx跟后端connect较慢导致
strace排查
strace用来跟踪系统调用,并可以打印丰富的信息,比如系统调用发生的时间,调用耗时,传参内容,调用返回结果等等
strace常用参数
-tt打印系统调用发生的时间
-T打印系统调用耗时
-e <name> trace特定系统调用,比如-e open,read,write,close追踪文件的相关调度
-s <size> 打印的最大长度
案例:PHP进程hang住
futex表示很可能发生死锁
strace可以打印出参数值
uaddr 0x398ad53594
op FUTEX_WAIT_PRIVATEhttp://blogimages.oss.aliyuncs.com/web_problem_strace_2.pngval 2
timeout null
可以用strace来验证系统调用耗时,在一次排查PHP问题时,发现PHP处理请求过慢,strace -T 发现是flock调用过慢
案例:系统负载特别高,能到200多,CPU使用率也特别高,到90%左右,strace没看到什么问题
分析
strace只能追踪系统调用,普通函数是无法trace的
CPU很高,说明程序一直在跑,但strace没有异常,说明是用户态代码在消耗CPU
问题必定出现在自己的程序逻辑上
结论
系统上线更换配置文件,导致程序逻辑出错
tcpdump网络抓包
tcpdump是一款强大的网络抓包工具,有时需要确认客户端到nginx或者nginx到后端PHP之间传递的数据是否正确,可以通过抓包对比。同时,对于慢响应,慢连接等网络问题,更需要要tcpdump
tcpdump具有强大的表达式机制
tcpdump host 10.16.15.41 and port 80 and tcp
tcpdump ’tcp[tcpflags] & (tcp-syn|tcp-fin)!=0’
tcpdump -w 写文件
tcpdump -r 读文件
wireshark
案例:还记得刚才提到的慢连接吗?nginx的access_log显示请求响应时间为3s多,而后端却是以毫秒计,那到底哪里慢.
其实慢连接的问题,我们线上经常遇到,只是大家没注意或者没在意,通常nginx做反向代理连接后端时,PHP程序在访问后端资源时,或PHP用CURL请求其他接口时,经常出现慢连接的情况
慢连接的根本原因
服务端listen时,设置的backlog太小,导致连接队列很小
连接队列满时,对于新的连接请求,服务端会直接丢失syn包
syn包初始重传时间为3s
具体原因可以参考《TCP三次握手之backlog》
综合案例分析
PHP升级的时候,开始运行正常,几天后,系统负载突然上升,达到200-400左右,CPU使用不高,内存使用不高,netstat发现大量PHP进程处于CLOSE_WAIT状态。
排查error_log,access_log没有发现问题
nginx不跟PHP在同一台机器上,暂时无法查看其error_log
CPU,内容都不高,为什么负载这么高
CLOSE_WAIT怎么造成的
从CLOSE_WAIT入手
TCP的关闭过程
被动关闭的一方,在接收到对方的FIN后,发送自己的FIN前的状态为CLOSE_WAIT
系统调用close,关闭连接,发送FIN
strace分析
从CLOSE_WAIT的状态来看,PHP应该是没有调用close函数导致,程序可能是因为某种原因塞住,从而无法调用CLOSE
通过strace查看PHP进程到底在哪里堵塞,则可查明原因
strace发现PHP进程没有堵塞
但是write函数调用失败
Broken pipe 说明连接已经关闭
调用close了
为什么连接已经关闭?难道是PHP处理太慢导致nginx超时?
从strace上看到,PHP从accept到close,总共耗时1ms左右。nginx不可能超时
tcpdump抓包
用wireshark查看
异常1
三次握手nginx发送syn,PHP响应ACK(应该是SYN+ACK)
异常2
NGINX发送FIN关闭连接后,PHP没有发送FIN
异常3
第二个连接的SYN,PHP同样只返回ACK,但ack number确认是上一个连接
结合strace和tcpdump
strace
tcpdump
accept 13:30:16,sun:13:27:57
accept 调用比接收到SYN晚2分钟多
backlog设置过大
SYN三次握手完成以后,socket放到连接队列
accept从连接队列获取socket
如果队列过大,等到accept消费到这个socket时,可能已经超时关闭连接
队列中关闭的连接处于CLOSE_WAIT 状态
PHP写一个关闭的连接,会报错Broken Pipe
总结
实际问题重视很复杂
找到正确的方向很重要
没有线索时多看看log,系统状态
学会用统计数据来对比
熟悉几个常用的工具
耐心