Arthas
Java 应用诊断利器,案例分析与破案实战内容最精彩。
1.简介
Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常、监测方法执行耗时、类加载信息等,大大提升线上问题排查效率。
2.背景
通常,本地开发环境无法访问生产环境。如果在生产环境中遇到问题,则无法使用 IDE 远程调试。更糟糕的是,在生产环境中调试是不可接受的,因为它会暂停所有线程,导致服务暂停。
开发人员可以尝试在测试环境或者预发环境中复现生产环境中的问题。但是,某些问题无法在不同的环境中轻松复现,甚至在重新启动后就消失了。
如果您正在考虑在代码中添加一些日志以帮助解决问题,您将必须经历以下阶段:测试、预发,然后生产。这种方法效率低下,更糟糕的是,该问题可能无法解决,因为一旦 JVM 重新启动,它可能无法复现,如上文所述。
Arthas 旨在解决这些问题。开发人员可以在线解决生产问题。无需 JVM 重启,无需代码更改。 Arthas 作为观察者永远不会暂停正在运行的线程。
3.Arthas(阿尔萨斯)能为你做什么?
当你遇到以下类似问题而束手无策时,Arthas 可以帮助你解决:
- 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
- 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
- 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
- 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
- 是否有一个全局视角来查看系统的运行状况?
- 有什么办法可以监控到 JVM 的实时运行状态?
- 怎么快速定位应用的热点,生成火焰图?
- 怎样直接从 JVM 内查找某个类的实例?
4.Docker容器实例里安装Arthas
进入应用容器实例
// 查看应用容器实例id
$ docker ps | grep insurance
b4126d9b72a6 hub.uban360.com/basic-x86/jar:202112201122 "sh run-java.sh" 2 hours ago Up 2 hours access_insurance-content_1
// 通过命令行进入应用容器实例
$ docker exec -it b4126d9b72a6 bash
安装Arthas
// 1.解压arthas集合归档包
unzip ~/arthas-packaging-3.5.0-bin.zip
// 2.下载arthas-boot.jar
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
// 使用as.sh
curl -L https://arthas.aliyun.com/install.sh | sh
Arthas命令列表
Arthas命令列表
单个命令类似积木,搭在一起功能会更加强大,可以高效的解决问题。
本章节内容可先略过,可以直接进入案例分析与破案实战。
基础命令
- help - 查看命令帮助信息
jvm 相关
- dashboard - 当前系统的实时数据面板
- JVM 内部线程、堆内存/非堆内存信息、运行时属性
- jvm - 查看当前 JVM 的信息
- 运行时属性、类加载统计、编译统计、垃圾收集器统计、内存管理者、内存统计、操作系统信息、线程统计、文件描述符相关
- memory - 查看 JVM 的内存信息
- 堆内存、非堆内存信息
- thread - 查看当前 JVM 的线程堆栈信息
- sysenv - 查看当前 JVM 的环境属性
- sysprop - 查看当前 JVM 的系统属性
- vmtool - 从 jvm 里查询内存对象,强制 GC 等功能
- getstatic - 查看类的静态属性
- ognl - 执行 ognl 表达式
- vmoption - 查看和修改 JVM 里诊断相关的 option
class/classloader 相关
- classloader - 查看 classloader 的继承树,urls,类加载信息,使用 classloader 去 getResource
- jad - 反编译指定已加载类的源码
- sc - 查看 JVM 已加载的类信息
- sm - 查看已加载类的方法信息
monitor/watch/trace 相关
注意:请注意,这些命令,都通过字节码增强技术来实现的,会在指定类的方法中插入一些切面来实现数据统计和观测。因此,在线上、预发使用时,请尽量明确需要观测的类、方法以及条件,诊断结束要执行
stop
或将增强过的类执行reset
命令。
- monitor - 方法执行监控
- stack - 输出当前方法被调用的调用路径
- 很多时候我们都知道一个方法被执行,但这个方法被执行的路径非常多,或者你根本就不知道这个方法是从哪里被执行了。
- trace - 方法内部调用路径,并输出方法路径上的每个节点上耗时
- 请求调用耗时很长问题剖析
- watch - 方法执行数据观测
- 让你能方便的观察到指定函数的调用情况。能观察到的范围为:
返回值
、抛出异常
、入参
,通过编写 OGNL 表达式进行对应变量的查看。
- 让你能方便的观察到指定函数的调用情况。能观察到的范围为:
- tt - 方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测
watch
虽然很方便和灵活,但需要提前想清楚观察表达式的拼写,这对排查问题而言要求太高,因为很多时候我们并不清楚问题出自于何方,只能靠蛛丝马迹进行猜测。- 这个时候如果能记录下当时方法调用的所有入参和返回值、抛出的异常会对整个问题的思考与判断非常有帮助。
profiler/火焰图
- profiler - 使用async-profiler对应用采样,生成火焰图
- 支持生成应用热点的火焰图。本质上是通过不断的采样,然后把收集到的采样结果生成火焰图。
- jfr - 动态开启关闭 JFR 记录
- Java Flight Recorder (JFR) 是一种用于收集有关正在运行的 Java 应用程序的诊断和分析数据的工具。它集成到 Java 虚拟机 (JVM) 中,几乎不会造成性能开销,因此即使在负载较重的生产环境中也可以使用。
- 支持在程序动态运行过程中开启和关闭 JFR 记录。 记录收集有关 event 的数据。事件在特定时间点发生在 JVM 或 Java 应用程序中。每个事件都有一个名称、一个时间戳和一个可选的有效负载。负载是与事件相关的数据,例如 CPU 使用率、事件前后的 Java 堆大小、锁持有者的线程 ID 等。
配置选项
- options - 查看或设置 Arthas 全局开关
5.案例分析与破案实战
警察要想破案,需要在案发现场收集更多的线索。
警察抓犯人,收集线索是破案的关键所在。
实践出真知识,真本领!
【3分钟内解决问题】
待补充完善。。。
// 查看方法调用的出入参、异常
watch java.lang.String toString '{params, returnObj}' -x 2
两个不同的GAV-classifier依赖冲突,引起NoSuchMethodError
Maven依赖的三坐标体系GAV(G-groupId,A-artifactId,V-version)
classifier
通常用于区分从同一POM构建的具有不同内容的构件物(artifact)。它是可选的,它可以是任意的字符串,附加在版本号之后。
【案例1】某应用依赖两个GAV-classifier不同的snakeyaml.jar,引起NoSuchMethodError
完整的详细步骤,请参见原文
1.完整的异常调用栈【从案发现场找线索】
警察抓犯人,收集线索是破案的关键所在。
2.关键的异常日志
从异常信息中收集有用的线索。
java.lang.NoSuchMethodError: org.yaml.snakeyaml.constructor.SafeConstructor.<init>(Lorg/yaml/snakeyaml/LoaderOptions;)V
at org.apache.dubbo.rpc.cluster.router.condition.config.model.ConditionRuleParser.parse(ConditionRuleParser.java:44)
3.案发现场的源代码
org.apache.dubbo.rpc.cluster.router.condition.config.model.ConditionRuleParser.parse(ConditionRuleParser.java:44)
- jad - 反编译指定已加载类的源码
[arthas@41915]$ jad org.apache.dubbo.rpc.cluster.router.condition.config.model.ConditionRuleParser parse
4.使用Arthas命令确认已加载的类信息和方法信息
破案杀手锏
[arthas@41915]$ sc -d org.yaml.snakeyaml.constructor.SafeConstructor
class-info org.yaml.snakeyaml.constructor.SafeConstructor
code-source /Users/lihuagang/.m2/repository/org/yaml/snakeyaml/1.23/snakeyaml-1.23-android.jar
name org.yaml.snakeyaml.constructor.SafeConstructor
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name SafeConstructor
modifier public
annotation
interfaces
super-class +-org.yaml.snakeyaml.constructor.BaseConstructor
+-java.lang.Object
class-loader +-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@4d95d2a2
classLoaderHash 18b4aac2
Affect(row-cnt:2) cost in 34 ms.
[arthas@41915]$ sm org.yaml.snakeyaml.constructor.SafeConstructor <init>
org.yaml.snakeyaml.constructor.SafeConstructor <init>()V
Affect(row-cnt:2) cost in 55 ms.
[arthas@41915]$ sm -d org.yaml.snakeyaml.constructor.SafeConstructor <init>
declaring-class org.yaml.snakeyaml.constructor.SafeConstructor
constructor-name <init>
modifier public
annotation
parameters
exceptions
classLoaderHash 18b4aac2
- 通过
sc -d org.yaml.snakeyaml.constructor.SafeConstructor
,可以看到其加载自snakeyaml-1.23-android.jar
。 - 通过
sm org.yaml.snakeyaml.constructor.SafeConstructor <init>
,可以看到没有org.yaml.snakeyaml.constructor.SafeConstructor#SafeConstructor(org.yaml.snakeyaml.LoaderOptions)
构造函数的方法签名。 - 综上所述,与异常信息完全符合。
5.解决方法
snakeyaml-1.23-android.jar
依赖排除掉,搞定。
祝大家玩得开心!ˇˍˇ
广益,杭州