【Java】排障方法论
大神文章的总结。整理人: pierre
@[toc]
机器异常、代码Bug、业务逻辑不当、开源组件使用姿势不对等等都会造成我们的现网后台服务不稳定,甚至是出现严重的服务挂掉的情况。当面对如此复杂的现网环境时,我们需要有一个清晰的问题排查思路,有章可循方能行之有度。总结一些问题排查的思路。
一、备份现场
- 问题出现的前后几分钟往往比较关键
- 要全:尽可能地把对问题分析有帮助的现场信息都保留备份
- 要快:系统在异常情况下错误日志的输出会更加频繁,可能覆盖掉关键日志
1、备份应用日志
应用日志是最佳切入问题的点:
- 一般系统出现问题时都会有相应的异常日志输出
- 能够定位到具体的代码片段,缩小问题排查范围
2、记录问题发生的时间
所有的信息都能通过时间这个维度刻画
- 问题发生时间点基础资源是否有波动(网卡流量飙升、CPU利用率飙升等)
- 是否存在变更操作,系统是否做过升级
3、备份GC日志
JVM的垃圾回收线程优先级高于应用线程
一旦出现频繁FULL GC的情况,应用级别的线程基本处于挂起状态,有时甚至连应用日志也不会输出。
长时间的FULL GC就意味着系统无响应,对外表现出来的现象就是超时及不可用
判断是否需要进行JVM调优
4、监控基础资源利用率曲线
- 可视化的曲线
- 峰值以及走势
过与前一天或者前一周相同时刻的曲线进行对比,为解决问题提供一个良好的参照
5、获取堆栈快照信息
jstack ${pid}> ${pid}.jstack
- 调用栈上下文
- 线程的状态(是否阻塞、僵死、死锁)信息
6、Dump内存信息
jmap -dump:live,format=b,file=${pid}.hdump.bin ${pid}
可以分析:
- 内存泄漏
- 对象回收不掉导致垃圾回收频繁
二、分析问题
计算机的四个核心组成部分:CPU、内存、网络、磁盘
1、CPU
综合CPU利用率高
- 非CPU密集型应用CPU突然飙高一定是系统存在异常情况
- CPU密集型应用则需要考量是否有优化的空间
排查思路:
top -Hp $pid
列出当前进程对应的线程占用CPU情况- 把占用CPU资源最高的线程ID(十进制)转换成十六进制的值
- 备份好的堆栈信息pid.jstack找到该十六进制值所对应的线程调用栈
一般当前调用栈所执行的代码就是导致CPU高的根源。当然方法调用可能存在系统调用的情况,这部分信息可能在堆栈中体现不出来。这种情况还需要通过系统性能分析工具perf、strace等进一步统计分析,找出消耗CPU最多的系统调用函数
优化建议:
- 内核消耗优化:
- 是否存在过多的系统调用:批量化的方式减低系统调用的次数
- 是否系统中的线程切换太过频繁:单线程+队列的模型降低线程间的切换和竞争带来的开销
- 算法优化:
加密解密/压缩解压等涉及数学运算的方法比较消耗CPU- 更优的算法
- 空间换时间的方式
- 终极优化:横向扩展机器数量、纵向升级服务器硬件能力
单核CPU利用率高
排查思路:
top -Hp $pid
再按1能查看到每个CPU核心的使用情况。
如果某个CPU利用率到90%以上的情况,通常是单个线程不断地干活导致的。
优化建议
- 数据同步并行化
- 对于数据备份或者同步的场景为了保证主备机器间的一致性,使用单线程
- 将不同的数据类型从源头区分开,在数据同步的时候分发到不同的复制线程并行化处理
- 业务拆解并行化
- 对现有的业务执行路径进行切分,提取出可以并行的
2、内存
jstat -gcutil ${pid} 3s 10
频繁FULL GC
- 每次执行垃圾回收算法后并没有腾出多少可用空间
- 内存占用空间依旧超出了触发垃圾回收的临界值马上又进入下一轮垃圾回收过程
- 频繁的执行垃圾回收算法会导致CPU利用率的飙升,程序表现僵死
排查思路:定位无法被回收的对象
- 开发机:MAT + hdump.bin文件
- 内存过大: 修改MemoryAnalyzer.ini
- mini版:
jmap -histo:live ${pid} |sort -k 3 -g -r|less
查看占内存最大的数据结构
优化建议
- 限制造成频繁GC的对象数量
- 使用队列的地方尽可能采用有界队列
- 异步化调用时信息缓存到特定的集合容器,需要限流控制
内存空间够,依旧触发了Full GC
因为碎片的原因导致没有一块连续足够大的可用内存空间,所以必须进行full gc 进行内存回收和碎片的整理,这个过程在采用大内存的场景下回收效率相当慢
优化建议
- 采用堆外内存替代堆内存(待深入)
后台IO高负载造成的长时间JVM GC停顿
因为IO原因导致卡在写gc统计日志上,就会导致系统的长时间不响应。
3、网络
超时严重
排查思路
一般网络延迟情况:
- IDC ~0.1ms
- 同城跨IDC ~5ms
- 异地跨IDC ~30ms-100ms
这种场景可以通过ping上下游系统服务器ip来进一步确认网络是否通畅,是否超出正常预期
优化建议
- 数据重复率可能陡增,做好去重设计
发送缓冲区积压
有些场景下自身系统各项资源指标正常,但是整体的吞吐量就是上不来,可能是这个问题引起。
排查思路
netstat -nap|grep $pid
查看链接情况- 多次执行该命令看到发送区的数值几乎没有变化,说明发送缓冲区积压
- 对方接收系统可能出现异常,导致我方发送不出去
优化建议
- 限流保护
- 防止击垮下游系统
- 防止己方系统中异步请求积压,导致GC
- 持久化落地
- 文件落地等应急措施
- 防止下游系统故障影响己方上游系统
接收缓冲区积压
netstat -nap|grep $pid
- 积压在缓冲区的数据处于内核态
- 缓冲区积压只是表象:
- 用户态内存空间不足?
- 程序僵死?
- CPU利用率异常?
- Full GC?
4、磁盘
IO Util高
iostat -x
%util
超过70%就需要重视
需要对热点代码进行追踪:
iotop
查看到按照%io占用百分比排列的线程- ID(十进制)转换成十六进制的值
- pid.jstack找到十六进制值对应的线程调用栈,深入分析
- 点赞
- 收藏
- 关注作者
评论(0)