Arthas线上服务诊断利器

一、简介

在这里插入图片描述

Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。

Arthas(阿尔萨斯)能为你做什么?

  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  5. 是否有一个全局视角来查看系统的运行状况?
  6. 有什么办法可以监控到 JVM 的实时运行状态?
  7. 怎么快速定位应用的热点,生成火焰图?
  8. 怎样直接从 JVM 内查找某个类的实例?

二、下载、启动、退出

直接访问下面连接下载jar包

https://arthas.aliyun.com/arthas-boot.jar

离线模式,要下载全量包

链接: 全量包

1、启动:
需先启动我们要监控的项目。

1、然后再启动arthas.jar

java -jar arthas-boot.jar
// 更换端口
java -jar arthas-boot.jar --telnet-port 9998 --http-port -1

2、然后根据提示选择我们要诊断的服务。输入数字,回车选择

在这里插入图片描述启动完成展示
在这里插入图片描述可以输入上面ip地址打开网页版
在这里插入图片描述
可以填入 IP,远程连接其它机器上的 arthas。

2、退出

如果只是退出当前的连接,可以用quit或者exit命令。Attach 到目标进程上的 arthas 还会继续运行,端口会保持开放,下次连接时可以直接连接上。

如果想完全退出 arthas,可以执行stop命令。


如果报错:java.util.ServiceConfigurationError: com.sun.tools.attach.spi.AttachProvider: Provider sun.tools.attach.WindowsAttachProvider could not be instantiated

就将 jdk/jre/bin/attach.dll,复制到 jre/bin目录下重新运行即可

如我的目录
在这里插入图片描述

三、命令理解与示例

以下示例中 com.example.demo.api.TestController是类路径,test是方法,test1是方法里的方法,自己要注意看

3.1、基本命令

  • base64 - base64 编码转换,和 linux 里的 base64 命令类似
  • cat - 打印文件内容,和 linux 里的 cat 命令类似
  • cls - 清空当前屏幕区域
  • echo - 打印参数,和 linux 里的 echo 命令类似
  • grep - 匹配查找,和 linux 里的 grep 命令类似
  • help - 查看命令帮助信息
  • history - 打印命令历史
  • pwd - 返回当前的工作目录,和 linux 命令类似
  • `quit - 退出当前 Arthas 客户端,其他 Arthas 客户端不受影响
  • reset - 重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端关闭时会重置所有增强过的类
  • session - 查看当前会话的信息
  • stop - 关闭 Arthas 服务端,所有 Arthas 客户端全部退出
  • tee - 复制标准输入到标准输出和指定的文件,和 linux 里的 tee 命令类似
  • version - 输出当前目标 Java 进程所加载的 Arthas 版本号
  • keymap - Arthas 快捷键列表及自定义快捷键
    在这里插入图片描述

3.2、JVM常用命令

1、dashboard - 当前系统的实时数据面板

在这里插入图片描述


2、thread - 查看当前 JVM 的线程堆栈信息

参数说明

参数名称 参数说明
id 线程 id
[n:] 指定最忙的前 N 个线程并打印堆栈
[b] 找出当前阻塞其他线程的线程
[i ] 指定 cpu 使用率统计的采样间隔,单位为毫秒,默认值为 200
[–all] 显示所有匹配的线程

示例
写个死循环
在这里插入图片描述

1、没有参数时,显示第一页线程的信息
默认按照 CPU 增量时间降序排列,只显示第一页数据。

$ thread   # 展示第一页线程

在这里插入图片描述2、查看详情,可以找到死循环代码的地方

thread id  # 显示指定线程的运行堆栈

在这里插入图片描述3、thread -n 3 查看最忙的前 N 个线程

$ thread -n 3 # 展示前3个最忙的线程,3随便换

在这里插入图片描述4、其他常用命令

$ thread --all, 显示所有匹配的线程

$ thread -b, 找出当前阻塞其他线程的线程

$ thread -i 1000, 统计最近 1000ms 内的线程 CPU 时间

3、ognl - 执行 ognl 表达式

1、调用静态函数

$ ognl '@[email protected]("hello")'

程序里就会输出hello
在这里插入图片描述

2、获取静态类的静态字段
在这里插入图片描述

$ ognl '@包名/类名@静态方法/字段'

在这里插入图片描述
还可以操作静态字段,如输出大小、添加值等等。

在这里插入图片描述


3.3、class/classloader 常用命令

1、sc - 查看 JVM 已加载的类信息(search Class找类)

1、查看这个包有哪些类,支持模糊搜索

$ sc com.example.demo.*

在这里插入图片描述

$ sc -d com.example.demo.*  # -d打印类的详细信息
$ sc -d -f com.example.demo.*  # -f打印出类的 Field 信息

2、sm - 查看已加载类的方法信息(search Method找方法)

1、查找这个类有哪些方法,支持模糊

$ sm com.example.demo.api.*

在这里插入图片描述

$ sm -d com.example.demo.api.*  # -d打印方法的详细信息

3、jad - 反编译指定已加载类的源码

感觉线上的代码和当前不一致时可以使用

$ jad  类名

在这里插入图片描述

只需要某个方法时,指定方法名

$ jad 类名 方法名

在这里插入图片描述
将反编译的文件放到某个目录,目录是相对于项目的位置

# --source-only反编译时只显示源代码
$ jad --source-only com.example.demo.api.TestController > ./newClass/TestController.java

在这里插入图片描述


4、mc - 内存编译器,内存编译.java文件为.class文件

1、将某个java文件编译到某个.class文件。

先修改刚刚编译下来的java文件,修改后反编译回去

$ vim ./newClass/TestController.java  # 修改代码

编译java文件

#-d命令指定输出目录
$ mc ./newClass/TestController.java -d ./newClass

在这里插入图片描述


5、redefine - 加载外部的.class文件,redefine 到 JVM 里

使用redefine命令加载新的字节码

$ redefine ./newClass/com/example/demo/api/TestController.class

在这里插入图片描述


通过测试上述流程发现可实现动态改线上代码 通过jad反编译出来代码,修改完后再编译为class文件,最后通过redefine加载class文件到jvm里

3.4、monitor/watch/trace 相关命令

1、monitor - 方法执行监控

可用于监控方法执行时间,用monitor的监控avg-rt(平均执行时间)看

参数说明:

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
condition-express 条件表达式
[E] 开启正则表达式匹配,默认为通配符匹配
[c:] 统计周期,默认值为 120 秒
[b] 在方法调用之前计算 condition-express
# -c 5秒刷新一次
$ monitor 类名 方法名  -c 5

在这里插入图片描述
监控的维度说明:

监控项 说明
timestamp 时间戳
class Java 类
method 方法(构造方法、普通方法)
total 调用次数
success 成功次数
fail 失败次数
rt 平均执行时间
fail-rate 失败率

2、watch - 方法执行数据观测

让你能方便的观察到指定函数的调用情况。能观察到的范围为:返回值、抛出异常、入参,通过编写 OGNL 表达式进行对应变量的查看。

参数说明:

watch 的参数比较多,主要是因为它能在 4 个不同的场景观察对象

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 函数名表达式匹配
express 观察表达式,默认值:{params, target, returnObj}
condition-express 条件表达式
[b] 在函数调用之前观察
[e] 在函数异常之后观察
[s] 在函数返回之后观察
[f] 在函数结束之后(正常返回和异常返回)观察
[E] 开启正则表达式匹配,默认为通配符匹配
[x:] 指定输出结果的属性遍历深度,默认为 1,最大值是 4

特别说明:

  • watch 命令定义了 4 个观察事件点,即 -b 函数调用前,-e 函数异常后,-s 函数返回后,-f 函数结束后
  • 4 个观察事件点 -b、-e、-s 默认关闭,-f 默认打开,当指定观察点被打开后,在相应事件点会对观察表达式进行求值并输出
  • 这里要注意函数入参和函数出参的区别,有可能在中间被修改导致前后不一致,除了 -b 事件点 params 代表函数入参外,其余事件都代表函数出参
  • 当使用 -b 时,由于观察事件点是在函数调用前,此时返回值或异常均不存在
  • 在 watch 命令的结果里,会打印出location信息。location有三种可能值:AtEnter,AtExit,AtExceptionExit。对应函数入口,函数正常 return,函数抛出异常。

示例:
在这里插入图片描述

1、查看方法返回值

$ watch com.example.demo.api.TestController test

在这里插入图片描述

2、查看方法入参

# 观察表达式,默认值是{params, target, returnObj}
# -x 属性遍历深度
# -b 在函数调用之前观察
$ watch com.example.demo.api.TestController test "{params,returnObj}" -x 2  -b

我的传参是123
在这里插入图片描述

3、同时观察函数调用前和函数返回后

#-x 属性遍历深度
#-b 在函数调用之前观察
#-s 在函数返回之后观察
#-n 只执行两次
$ watch com.example.demo.api.TestController test  -x 2  -b -s -n 2

在这里插入图片描述

4、条件表达式的例子
只观察参数小于0的

# 观察表达式,默认值是{params, target, returnObj}
# 条件第一个参数<0
#-x 属性遍历深度
watch com.example.demo.api.TestController test  "{params,returnObj}" "params[0]<0" -x 2

在这里插入图片描述


3、trace - 方法内部调用路径,并输出方法路径上的每个节点上耗时

trace 命令能主动搜索 class-pattern/method-pattern 对应的方法调用路径,渲染和统计整个调用链路上的所有性能开销和追踪调用链路。

参数说明:

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
condition-express 条件表达式,使用ognl表达式
[E] 开启正则表达式匹配,默认为通配符匹配
[n:] 命令执行次数
#cost 方法执行耗时,单位是毫秒

示例:
在这里插入图片描述
1、监控方法的链路与耗时

$ trace 类名 方法名

如: trace com.example.demo.api.TestController test

在这里插入图片描述

$ trace 类名 方法名 '#cost>1000'  #只监控耗时超过1m的

如:trace com.example.demo.api.TestController test '#cost>1000' 

4、stack - 输出当前方法被调用的调用路径

很多时候我们都知道一个方法被执行,但这个方法被执行的路径非常多,或者你根本就不知道这个方法是从那里被执行了,此时你需要的是 stack 命令。

参数说明:

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
condition-express 条件表达式,使用ognl表达式
[E] 开启正则表达式匹配,默认为通配符匹配
[n:] 命令执行次数

1、监控test1方法在哪里被调用

$ stack com.example.demo.api.TestController test1

在这里插入图片描述

# 据条件表达式来过滤
$ stack com.example.demo.api.TestController test1 'params[0]<0' -n 2 
# 据执行时间来过滤
$ stack com.example.demo.api.TestController test1 '#cost>1000'

5、tt - 方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测

watch 虽然很方便和灵活,但需要提前想清楚观察表达式的拼写,这对排查问题而言要求太高,因为很多时候我们并不清楚问题出自于何方,只能靠蛛丝马迹进行猜测。

这个时候如果能记录下当时方法调用的所有入参和返回值、抛出的异常会对整个问题的思考与判断非常有帮助。

于是乎,TimeTunnel 命令就诞生了。

1、记录调用

$ tt -t com.example.demo.api.TestController test

在这里插入图片描述如果发现这里是true,就说明这个时候发生了异常

表格字段说明

表格字段 字段解释
INDEX 时间片段记录编号,每一个编号代表着一次调用,后续 tt 还有很多命令都是基于此编号指定记录操作,非常重要。
TIMESTAMP 方法执行的本机时间,记录了这个时间片段所发生的本机时间
COST(ms) 方法执行的耗时
IS-RET 方法是否以正常返回的形式结束
IS-EXP 方法是否以抛异常的形式结束
OBJECT 执行对象的hashCode(),注意,曾经有人误认为是对象在 JVM 中的内存地址,但很遗憾他不是。但他能帮助你简单的标记当前执行方法的类实体
CLASS 执行的类名
METHOD 执行的方法名

2、显示所有已经记录的列表

$ tt -l

在这里插入图片描述3、查看调用信息

如果观察记录列表发生异常了,可以用这个查看入参与返回值,从而排查问题

$ tt -i 1001

在这里插入图片描述


3.5、profiler/火焰图

profiler 命令支持生成应用热点的火焰图。本质上是通过不断的采样,然后把收集到的采样结果生成火焰图。

在这里插入图片描述
1、命令

$ profiler start  # 启动

$ profiler getSamples # 获取已采集的 sample 的数量

$ profiler status #查看 profiler 状态

$ profiler stop --format html #停止 profiler,生成 html 格式,可以用--format参数指定生成格式

访问 http://localhost:3658/arthas-output/ 查看到arthas-output目录下面的 profiler 结果:

在这里插入图片描述

四、idea的arthas插件

1、插件市场搜索arthas idea

将光标放置在具体的类、字段、方法上面 右键选择需要执行的命令,部分会有窗口弹出、根据界面操作获取命令;部分直接获取命令复制到了剪切板 ,自己启动 arthas 后粘贴命令即可执行。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_48721706/article/details/126696465