定位问题的先决条件
需要有详细的日志记录,提前告警的监控平台,事发现场保留
日志 :业务日志,中间件日志
监控 :CPU、内存、磁盘、网络,类加载、GC、线程等
快照 :-XX:+HeapDumpOnOutOfMemoryError 和 -XX:HeapDumpPath
分析问题,解决问题的思路
经验+直觉,快速定位 > 逐一排查,传输链路 > 寻找规律 不要轻易怀疑监控。考虑资源。优先保证系统能正常运行。保留现场,事后排查定位问题。
逐一排查,传输链路,通过日志或工具逐一排查
- 内部原因,是否是客户端或者前端问题,程序发布后的Bug,回滚后可以立即解决
- 外部原因,比如服务,第三方服务,主机、组件的问题。
- 服务:错误日志邮件提醒或elk快速定位问题,查看gc日志
- 第三方服务:单独调用测试,联系第三方加急解决
- 主机: CPU相关问题,可以使用 top、vmstat、pidstat、ps 等工具排查; 内存相关问题,可以使用 free、top、ps、vmstat、cachestat、sar 等工具排查;IO 相关问题,可以使用 lsof、iostat、pidstat、sar、iotop、df、du 等工具排查;网络相关问题,可以使用 ifconfig、ip、nslookup、dig、ping、tcpdump、iptables等工具排查。
- 组件:查看日志输出,使用命令查看运行情况
- 因为系统资源不够造成系统假死的问题,通常需要先通过重启和扩容解决问题,之后再进行分析,系统资源不够,一般体现在 CPU 使用高、 内存泄漏或OOM 的问题、IO问题、网络相关问题这四个方面
分析问题的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| jps -v 查看java进程 jinfo -flags pid 查看运行参数 jstat -gc 8544 5000 100,将每隔5s采样一次pid为8544的gc,输出100次
jmap -dump:live,format=b,file=dump.hprof 29170 #生成虚拟机的内存转储快照 注意线上可能会触发线上gc jmap -heap 29170 jmap -histo:live 29170 | more jmap -permstat 29170
jstack -l 29170 |more 显示虚拟机的线程快照
df -h # 磁盘 free -m / -h # 内存 top cpu # cpu
复制代码
|
线上cpu100%报警(找出最耗时CPU进程-线程-堆栈-代码)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 方法1:原生工具,慢 top -c #显示完整信息 P:cpu使用排序 M:内存使用排序 top -Hp 10765 ,#显示一个进程的线程运行信息列表 -H 显示线程信息,-p指定pid & P printf "%x\n" 10804 转16进制 2f71 jstack 12084 | grep '0x2f71' -C5 --color 查看堆栈,找到线程在干嘛
方法2: 使用提前准备好的sh脚本,可以一条命名查看当前出事的线程代码,快,推荐 sh show-busy-java-threads.sh > a.txt #查询java耗时线程前5个 sh show-busy-java-threads.sh -p > a.txt #查询指定进程
方法3: 使用arthas,工具内置很多功能,比如可以查看源码,判断是否发布成功,可以用来排查疑难问题 curl -O https://alibaba.github.io/arthas/arthas-boot.jar java -jar arthas-boot.jar dashboard thread -8 jad com.xx.xx.xx.xxximp 查看线上类代码 watch com.xx.xx.xx.xxximp doTask '{params}' '#cost>100' -x 2 #观察会慢在什么入参上,监控耗时超过100毫秒的 doTask方法的入参,并且输出入参,展开2层入参参数 ognl #查询某静态字段的值
定位到堆栈就可以定位到出问题代码的行号,然后找对应的发布分支代码该行号即可 复制代码
|
线上内存OOM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 某Java服务(假设PID=12084)出现了OOM,最常见的原因为: 1. 有可能是内存分配确实过小,而正常业务使用了大量内存 2. 某一个对象被频繁申请,却没有释放,内存不断泄漏,导致内存耗尽未调用close(),dispose()释放资源,例如:文件io,网络io 3. 某一个资源被频繁申请,系统资源耗尽,例如:不断创建线程(没有用线程池),不断发起网络连接等 总结:本身资源不够,申请资源太多,资源耗尽
分析工具: jvisualvm(直方图),MAT(优先,直方图,跟踪内存使用的引用关系),JProfiler
线下分析: 服务挂掉之后有保留文件:直接下载dump文件导入mat分析 java -jar -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=
线上分析: 1. 确认是不是内存本身就分配过小 jmap -heap 12084
2. 找到最耗内存的对象 jmap -histo:live 12084 | head -n 10 #该命令会强制执行一次fgc
jmap -dump:format=b,file=/opt/dump.hprof {pid} #以二进制输出档当前内存的堆情况, 然后可以导入MAT等工具进行 tar –czf dump.tar.gz dump.hprof
3. 确认进程创建的线程数,以及网络连接数,如果资源耗尽,也可能出现OOM ll /proc/17306/fd | wc -l ll /proc/17306/task | wc -l 复制代码
|
如何防止线上问题发生
数据库:上线一个定时监控和杀掉慢SQL的脚本。这个脚本每分钟执行一次,检测上一分钟内,有没有执行时间超过一分钟(这个阈值可以根据实际情况调整)的慢SQL,如果有大事务自己觉得该阈值的合理性,如果发现,直接杀掉这个会话
cpu或者内存的使用率上做报警,大于90%的时候可以dump和jstack一次,甚至jstat也可以做,然后95%的时候也同样执行一次,甚至98或者99的时候也可以做一次,这样不仅可以保留现场,同时还可以对比
完善的服务报错日志监控,可选elfk+日志监控或sentry
完善的流程机制。完善的主机,中间件监控报警机制
遇到过的线上问题以及解决思路
Zuul 网关不响应任何请求,zuul假死
App打不开,请求超时,访问数据库超时,数据库cpu飙升有规律,在某个时间点才飙升,去调度中心找该时间断的的定时任务,排查是异步转账开多了线程导致的
工具汇总
参考