Java线上监控诊断产品Arthas(续集)
- 前言
- 1.auth指令
- 2.monitor指令
- 解读
- 3.classloader指令
- 场景
- 4.dump指令
- 场景
- 5.getstatic指令
- 场景
- 6.heapdump指令
- 场景
- 7.profiler指令
- 场景
- 8.sc指令
- 场景
- 9.trace指令
- 场景
前言
在去年,我发表了一片文章,Java线上监控诊断产品Arthas,今年又重新回顾了一下这门技术,发现了以前忽略了很多技术点,这篇文章用来作补充,并且思考了一下这些技术点可以用来哪些场景。
1.auth指令
在 attach 时,可以在命令行指定密码。比如:
java -jar arthas-boot.jar --password admin
- 可以通过 --username 选项来指定用户,默认值是arthas。
- 也可以在 arthas.properties 里中配置 username/password。命令行的优先级大于配置文件。
- 如果只配置username,没有配置password,则会生成随机密码,打印在~/logs/arthas/arthas.log中
本地连接不鉴权
为了方便本地开发和调试,Arthas 默认允许本地连接不进行鉴权。,在arthas.properties文件里有配置:
arthas.localConnectionNonAuth=true
这样,即使设置了密码,本地连接也无需进行认证。
telnet链接鉴权
远程连接后,需要输入auth指令,输入正确密码,这样才能执行arthas的命令。
[arthas@37430]$ auth admin
Authentication result: true
不然会提示:
Error! command not permitted, try to use 'auth' command to authenticates.
2.monitor指令
案例一:最简单的应用,-c参数:统计周期,默认为120秒,我这5秒一次
monitor -c 5 com.example.demo.controller.ArthasController test
输入之后界面这样,没有任何输出:
[arthas@2719]$ monitor -c 5 com.example.demo.controller.ArthasController test
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 220 ms, listenerId: 2
然后我在浏览器疯狂请求这个接口几次,看看输出:
timestamp class method total success fail avg-rt(ms) fail-rate
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2024-08-26 14:54:40 com.example.demo.controller.ArthasController test 1 1 0 5.53 0.00%
timestamp class method total success fail avg-rt(ms) fail-rate
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2024-08-26 14:54:45 com.example.demo.controller.ArthasController test 6 6 0 1.91 0.00%
timestamp class method total success fail avg-rt(ms) fail-rate
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2024-08-26 14:54:47 com.example.demo.controller.ArthasController test 9 9 0 0.13 0.00%
timestamp class method total success fail avg-rt(ms) fail-rate
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2024-08-26 14:54:55 com.example.demo.controller.ArthasController test 0 0 0 0.00 0.00%
案例二:
此指令还支持条件匹配式,我这里举个最简单的例子:
“params[0]<=2”,表示接口的第一个参数必须<=2
monitor -c 5 com.example.demo.controller.ArthasController test "params[0]<=2"
经过测试,我参数传2的时候,可以检测到,当我参数num传3的时候,就检测不到:
接口:http://172.16.72.132:8081/arthast/inputNum?num=3
timestamp class method total success fail avg-rt(ms) fail-rate
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2024-08-26 14:59:31 com.example.demo.controller.ArthasController test 1 1 0 0.21 0.00%
timestamp class method total success fail avg-rt(ms) fail-rate
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2024-08-26 14:59:41 com.example.demo.controller.ArthasController test 0 0 0 0.00 0.00%
解读
1.由上面的场景可以看出来,这个指令可以监控一个方法,在一定时间(-c控制)里被请求了多少次,可以用来压测、或者是热点接口监控,可以显示成功和失败率,并且可以显示平均 RT。
2.指令的条件匹配式,还可以帮助我们筛选想要的请求进来统计。
3.classloader指令
查看 classloader 的继承树,urls,类加载信息
-
classloader -l 按类加载实例进行统计
[arthas@2719]$ classloader -l name loadedCount hash parent BootstrapClassLoader 4393 null null com.taobao.arthas.agent.ArthasClassloader@dfd4a07 1702 dfd4a07 jdk.internal.loader.ClassLoaders$PlatformClassLoader@71423665 jdk.internal.loader.ClassLoaders$AppClassLoader@5c29bfd 47 5c29bfd jdk.internal.loader.ClassLoaders$PlatformClassLoader@71423665 jdk.internal.loader.ClassLoaders$PlatformClassLoader@71423665 115 71423665 null org.springframework.boot.loader.LaunchedURLClassLoader@9629756 10082 9629756 jdk.internal.loader.ClassLoaders$AppClassLoader@5c29bfd Affect(row-cnt:5) cost in 11 ms.
-
classloader -t 打印所有 ClassLoader 的继承树
[arthas@2719]$ classloader -t +-BootstrapClassLoader +-jdk.internal.loader.ClassLoaders$PlatformClassLoader@71423665 +-com.taobao.arthas.agent.ArthasClassloader@dfd4a07 +-jdk.internal.loader.ClassLoaders$AppClassLoader@5c29bfd +-org.springframework.boot.loader.LaunchedURLClassLoader@9629756 Affect(row-cnt:5) cost in 11 ms.
注意:上面打印出来的信息,尾部有@9629756 这种编号,这是类加载器的HashCode值,后面我们如果要指定类加载器,参数跟的就是这一串HashCode。
- classloader -c 显示类加载器位置,配合load参数,可以指定用某个类加载器去加载某个类。
[arthas@2719]$ classloader -c 9629756 --load com.example.demo.controller.FileController
load class success.
class-info com.example.demo.controller.FileController
code-source file:/data/demo-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/
name com.example.demo.controller.FileController
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name FileController
modifier public
annotation org.springframework.stereotype.Controller,org.springframework.web.bind.annotation.RequestMapping
interfaces
super-class +-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@9629756
+-jdk.internal.loader.ClassLoaders$AppClassLoader@5c29bfd
+-jdk.internal.loader.ClassLoaders$PlatformClassLoader@71423665
classLoaderHash 9629756
场景
可以让指定的 classloader 去 getResources,打印出所有查找到的 resources 的 url。并且还可以指定类类的加载器,对于ResourceNotFoundException比较有用。
4.dump指令
dump 已加载类的 bytecode 到特定目录
场景
将 JVM 中实际运行的 class 的 byte code dump 到指定目录,适用场景批量下载指定包目录的 class 字节码;如需反编译单一类、实时查看类信息,可参考 jad。
-
dump -d /xx dump到指定目录
[arthas@2719]$ dump -d /data com.example.demo.controller.UserInfoController HASHCODE CLASSLOADER LOCATION 9629756 +-org.springframework.boot.loader.LaunchedURLClassLoader@9629756 /data/org.springframework.boot.loader.LaunchedURLClassLoader-9629756/com/example/demo/controller/UserInfoController.class +-jdk.internal.loader.ClassLoaders$AppClassLoader@5c29bfd +-jdk.internal.loader.ClassLoaders$PlatformClassLoader@71423665 Affect(row-cnt:1) cost in 202 ms.
我们看看/data下面的目录文件:
[root@localhost data]# ll /data/ 总用量 100884 -rw-r--r--. 1 root root 141899 8月 2 23:45 arthas-boot.jar drwxr-xr-x. 2 root root 24 8月 4 01:46 arthas-output -rw-r--r--. 1 root root 99038636 8月 26 14:39 demo-0.0.1-SNAPSHOT.jar -rw-r--r--. 1 root root 179 8月 2 23:14 Dockerfile drwxr-xr-x. 2 root root 227 8月 26 11:04 logs -rw-r--r--. 1 root root 4120 8月 3 00:34 math-game.jar -rw-------. 1 root root 4097224 8月 26 14:59 nohup.out drwxr-xr-x. 3 root root 17 8月 26 15:50 org.springframework.boot.loader.LaunchedURLClassLoader-9629756 -rw-r--r--. 1 root root 598 8月 4 03:04 Test.class -rw-r--r--. 1 root root 446 8月 23 16:17 Test.java
org.springframework.boot.loader.LaunchedURLClassLoader-9629756,这个文件就是我们刚刚dump出来的目录,我们进入目录一直点下去,就能看到最终的class文件:
[root@localhost controller]# ll 总用量 4 -rw-r--r--. 1 root root 2010 8月 26 15:50 UserInfoController.class
-
dump --classLoaderClass sun.misc.Launcher$AppClassLoader UserInfoController
用–classLoaderClass参数+ ClassLoader 的类名来指定类加载器加载,这里ClassLoader 实例必须唯一。classLoaderClass 在 java 8 是 sun.misc.Launcher$AppClassLoader,而 java 11 的 classloader 是 jdk.internal.loader.ClassLoaders$AppClassLoader
5.getstatic指令
场景
想要知道某个类中的某个static修饰的字段的值是多少
示例:
我目前ArthasController 中的sum字段的值类型为Integer,值为6
[arthas@3126]$ getstatic com.example.demo.controller.ArthasController sum
field: sum
@Integer[6]
Affect(row-cnt:1) cost in 14 ms.
6.heapdump指令
场景
导出当前java的堆栈信息。
示例:
[arthas@3126]$ heapdump /data/testDump.hprof
Dumping heap to /data/testDump.hprof ...
Heap dump file created
cd到/data目录后,就可以看到了:
-rw-------. 1 root root 76268636 8月 26 16:32 testDump.hprof
PS:这里的/data目录(本文几乎所有目录),是虚拟机的目录,不是arthas的目录,我们虽然attach上了arthas,控制台变了,但是我们还是可以访问宿主机的目录。默认路径是你arthas-boot.jar文件的目录
7.profiler指令
场景
生成火焰图
示例:
1.开始采样
[arthas@3126]$ profiler start
Profiling started
2.停止,会自动保存
[arthas@3126]$ profiler stop
OK
profiler output file: /data/arthas-output/20240826-170650.html
3.查看
官方说的是用 http://localhost:3658/arthas-output/就能查看,但是我物理机去访问这个地址,ip和端口都没问题,但是就是访问不起,不知道啥情况, 最后只有将文件复制到Windows,然后用浏览器查看。
最后结果就长这样,目前还不知道这个图怎么看,后面学习了再补充上。
8.sc指令
场景
查看 JVM 已加载的类信息
可选参数-d,就是detail
[arthas@3126]$ sc -d com.example.demo.controller.UserInfoController
class-info com.example.demo.controller.UserInfoController
code-source file:/data/demo-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/
name com.example.demo.controller.UserInfoController
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name UserInfoController
modifier public
annotation org.springframework.web.bind.annotation.RestController,org.springframework.web.bind.annotation.RequestMapping
interfaces
super-class +-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@9629756
+-jdk.internal.loader.ClassLoaders$AppClassLoader@5c29bfd
+-jdk.internal.loader.ClassLoaders$PlatformClassLoader@71423665
classLoaderHash 9629756
Affect(row-cnt:1) cost in 110 ms.
可选参数-f,需要配合-d一起使用,显示字段信息。
9.trace指令
场景
输出方法内部调用路径,从上往下层级调用输出,并输出方法路径上的每个节点上耗时
[arthas@3126]$ trace com.example.demo.controller.UserInfoController fileUpload
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 1030 ms, listenerId: 1
`---ts=2024-08-26 18:03:41;thread_name=http-nio-8081-exec-8;id=30;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@2ca6546f
`---[75.03077ms] com.example.demo.controller.UserInfoController:fileUpload()
+---[98.91% 74.209617ms ] com.example.demo.service.UserInfoService:list() #36
`---[0.11% 0.084421ms ] com.example.demo.vo.ResultMsg:success() #37
这个接口的list方法耗时74.209617ms,占比98.91%,而ResultMsg:success()方法耗时0.084421ms,占比0.11%
以上就是对第一篇Java线上监控诊断产品Arthas内容的补充,如果后续有新的体会,出个3.0版本也说不一定,或者在本文继续补充。