Java 线上故障解决方案
一、前言:
我们在⽣产环境中,程序代码、硬件、⽹络、协作软件等任⼀因素,都会引发意想不到的问题,所以排查产线问题⽐较困难,所以问题的定位体现了⼀名⼯程师的基础能⼒,问题的解决则体现了⼯程师的技能素养。
二、线上常见问题
在这里插入图片描述
如出现 (CPU 占⽤率过⾼、磁盘使⽤率 100%、系统可⽤内存低、服务间调⽤时间过⻓、多线程并发异常、死锁等)
三、定位问题
方案 :
业务⽇志分析排查
通常情况下,⼤部分错误信息都会在⽇志上有所体现
在这里插入图片描述
通过日志可以发现出错误的位置是 第 33 行,报错 java.lang.OutOfMemoryError 错误, 因为我们看下 newFixedThreadPool 方法的源码,发现,线程池的工作队列直接 new 了一个 LinkedBlockingQueue,他是一个无界队列。如果任务较多并且执行较慢但话,队列可能会快速积压,撑爆内存导致 OOM
我们 ⼀定要在关键代码逻辑位置输出相关⽇志,尤其是在代码发⽣异常的时候,定要将⽇志输出到⽂件中,只有这样,才更利于我们的排查。
APM 分析排查
APM,全称 Application Performance Management,应⽤性能管理,⽬的是通过各种探针采集数据, 收集关键指标,同时搭配数据呈现以实现对应⽤程序性能管理和故障管理的系统化解决⽅案。通过分布 式链路调⽤跟踪系统,通过在系统请求中透传 trace-id,将所有相关⽇志进⾏聚合,然后⽇志统⼀采集 和分析后,以图形化的形式展示给⼯程师们,⽽他们在排查问题的时候,可以简单粗暴且直观的调度出 问题最根本的原因。
通常在分布式架构中,仅通过分析单个服务的⽇志信息是不够的,此时则需要 APM 进⾏全链路分析,通过请求链路监控,实时的发现链路中相关服务的异常情况。
在这里插入图片描述
⽬前市场上使⽤较多的链路跟踪⼯具有如下⼏个:
大众点评 CAT :GitHub - dianping/cat: Central Application Tracking
Apache Skywalking:skywalking.apache.org/
Pinpoint:pinpoint.com/product/for…
SpringCloud Zipkin:docs.spring.io/spring-clou…
物理环境排查
CPU 分析
CPU 使⽤率是衡量系统繁忙程度的重要指标。但是 CPU 使⽤率的安全阈值是相对的,取决于你的系统的 IO 密集型还是计算密集型。⼀般计算密集型应⽤CPU 使⽤率偏⾼load 偏低,IO 密集型相反。
top 命令是 Linux 下常⽤的 CPU 性能分析⼯具,能够实时显示系统中各个进程的资源占⽤状况,常⽤于服务端性能分析。
在这里插入图片描述
top 命令显示了各个进程 CPU 使⽤情况,⼀般 CPU 使⽤率从⾼到低排序展示输出。其中 LoadAverage 显示最近 1 分钟、5 分钟和 15 分钟的系统平均负载,上图各值为 3.4、3.31、3.46。
我们⼀般会关注 CPU 使⽤率最⾼的进程,正常情况下就是我们的应⽤主进程。第七⾏以下:各进
程的状态监控,参数说明:
PID : 进程 id
USER : 进程所有者的⽤户名
PR : 进程优先级
NI : nice 值。负值表示⾼优先级,正值表示低优先级
VIRT : 进程使⽤的虚拟内存总量,单位 kb
SHR : 共享内存⼤⼩
%CPU : 上次更新到现在的 CPU 时间占⽤百分⽐
%MEM : 进程使⽤的物理内存百分⽐
TIME+ : 进程使⽤的 CPU 时间总计,单位 1/100 秒
COMMAND : 命令名称、命令⾏
内存
内存是排查线上问题的重要参考依据,free 是显示的当前内存的使⽤,-h 表示⼈类可读性。
在这里插入图片描述
参数说明:
total :内存总数
used:已经使⽤的内存数
free:空闲的内存数
shared:被共享使⽤的物理内存⼤⼩
buffers/buffer:被 buffer 和 cache 使⽤的物理内存⼤⼩
available: 还可以被应⽤程序使⽤的物理内存⼤⼩
磁盘
在这里插入图片描述
⽹络
在这里插入图片描述
默认情况下,dstat 每秒都会刷新数据
三、Arthas 诊断命令
在这里插入图片描述
Arthas 是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱。
当你遇到以下类似问题而束手无策时,Arthas 可以帮助你解决:
这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
是否有一个全局视角来查看系统的运行状况?
有什么办法可以监控到 JVM 的实时运行状态?
怎么快速定位应用的热点,生成火焰图?
官方文档 :arthas.aliyun.com/doc/
安装
在这里插入图片描述
arthas 会列出已存在的 Java 进程,并提醒输⼊序号,键⼊回⻋,进⼊arthas 诊断界⾯。
arthas 常⻅命令介绍
jvm 查看当前 JVM 的信息
thread 查看当前 JVM 的线程堆栈信息,-b 选项可以⼀键检测死锁
trace ⽅法内部调⽤路径,并输出⽅法路径上的每个节点上耗时,服务间调⽤时间过⻓时使⽤
stack 输出当前⽅法被调⽤的调⽤路径
Jad 反编译指定已加载类的源码,反编译便于理解业务
logger 查看和修改 logger,可以动态更新⽇志级别。
四、JVM 问题定位命令
在 JDK 安装⽬录的 bin ⽬录下默认提供了很多有价值的命令⾏⼯具。每个⼩⼯具体积基本都⽐较⼩,因 为这些⼯具只是 jdk\lib\tools.jar 的简单封装。
其中,定位排查问题时最为常⽤命令包括:jps(进程)、jmap(内存)、jstack(线程)、jinfo(参 数)等。
jps:查询当前机器所有 Java 进程信息
jmap:输出某个 Java 进程内存情况
jstack:打印某个 Java 线程的线程栈信息
jinfo:⽤于查看 jvm
1. jps
jps ⽤于输出当前⽤户启动的所有进程 ID,当线上发现故障或者问题时,利⽤ jps 快速定位对应的 Java 进程 ID。
参数解释:
m:输出传⼊ main ⽅法的参数
l:输出完全的包名,应⽤主类名,jar 的完全路径名
在这里插入图片描述
当然,我们也可以使⽤ Linux 提供的查询进程状态命令也能快速获取 Tomcat 服务的进程 id。⽐如:
2. jmap
jmap(Java Memory Map)可以输出所有内存中对象的⼯具,甚⾄可以将 VM 中的 heap,以⼆进制输 出成⽂本,使⽤⽅式如下: jmap -heap:
注意:pid 通过 jps 命令得知
在这里插入图片描述
3. jstack
jstack⽤于打印某个 Java 线程的线程栈信息
举个栗⼦,某 Java 进程 CPU 占⽤率⾼,我们想要定位到其中 CPU 占⽤率最⾼的线程,如何定位?
3.1 利⽤ top 命令可以查出占 CPU 最⾼的线程 pid
在这里插入图片描述
3.2 占⽤率最⾼的线程 ID 为 22021,将其转换为 16 进制形式(因为 java native 线程以 16 进制形式输 出)
3.3 利⽤ jstack 打印出 Java 线程调⽤栈信息
在这里插入图片描述
4. jinfo
jinfo 可以⽤来查看正在运⾏的 java 应⽤程序的扩展参数,包括 Java System 属性和 JVM 命令⾏参数;也 可以动态的修改正在运⾏的 JVM ⼀些参数。
在这里插入图片描述
5. jstat
jstat 命令可以查看堆内存各部分的使⽤量,以及加载类的数量。
在这里插入图片描述
五、GC 分析
Gc 日志分析
Java 虚拟机 GC⽇志是⽤于定位问题重要的⽇志信息,频繁的 GC 将导致应⽤吞吐量下降、响应时间增 加,甚⾄导致服务不可⽤。
JVM 的 GC 日志的主要参数包括如下几个:
-XX:+PrintGC 输出 GC 日志
-XX:+PrintGCDetails 输出 GC 的详细日志
-XX:+PrintGCTimeStamps 输出 GC 的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出 GC 的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在进行 GC 的前后打印出堆的信息
-Xloggc:../logs/gc.log 日志文件的输出路径
IDEA 配置
在这里插入图片描述
我们可以在 Java 应⽤的启动参数中增加-XX:+PrintGCDetails 可以输出 GC 的详细⽇志,例外还可以增 加其他的辅助参数,如 -Xloggc 制定 GC ⽇志⽂件地址。如果你的应⽤还没有开启该参数,下次重启时请 加⼊该参数。
以后打印出来的日志为:
分析:
CMS GC ⽇志分析
Concurrent Mark Sweep(CMS)是⽼年代垃圾收集器,从名字(Mark Sweep)可以看出,CMS 收集 器就是“标记-清除”算法实现的,分为六个步骤:
初始标记(STW initial mark)
并发标记(Concurrent marking)
并发预清理(Concurrent precleaning)
重新标记(STW remark)
并发清理(Concurrent sweeping)
并发重置(Concurrent reset)
其中初始标记(STW initial mark) 和 重新标记(STW remark)需要“Stop the World”。
老年代的 GC 日志(CMS)
异常情况有: 伴随 prommotion failed,然后 Full GC: [prommotion failed:存活区内存不⾜,对象进⼊⽼年代,⽽此时⽼年代也仍然没有内存容纳对象,将 导致⼀次 Full GC] 伴随 concurrent mode failed,然后 Full GC: [concurrent mode failed:CMS 回收速度慢,CMS 完成前,⽼年代已被占满,将导致⼀次 Full GC] 频繁 CMS GC: [内存吃紧,⽼年代⻓时间处于较满的状态]
作者:ゞ浪人与酒丶
链接:https://juejin.cn/post/6925680285862330382
评论