Arthas(阿尔萨斯)定位线上问题

参考

官方文档:https://arthas.aliyun.com/doc/
用户案例:https://github.com/alibaba/arthas/issues?q=label%3Auser-case
一图流:https://github.com/alibaba/arthas/issues/1003
IDE插件官方文档:https://www.yuque.com/arthas-idea-plugin/help/pe6i45
一篇很好的总结:https://blog.csdn.net/u013735734/article/details/102930307?utm_source=app&app_version=4.13.0&code=app_1562916241&uLinkId=usr1mkqgl919blen

JVM工具专栏:https://cloud.tencent.com/developer/column/3195/tag-10688
https://www.cnblogs.com/chiangchou/p/jvm-4.html

实际使用案例

基础命令:https://arthas.aliyun.com/doc/advanced-use.html#id2

线上安装与挂载

wget https://alibaba.github.io/arthas/arthas-boot.jar
jps -ml
ps -ef | grep java
java -jar arthas-boot.jar {pid}

火焰图

参考

火焰图基本科普与讲解: https://www.ruanyifeng.com/blog/2017/09/flame-graph.html
定位不同问题的不同类型火焰图 + 安装使用教程:<https://www.infoq.cn/article/a8kmnxdhbwmzxzsytlga >
本机可以随意安装火焰图生成工具。用于线上环境时,需要看线上环境用的火焰图生成工具是什么,生成图示之后传到本机进行分析即可。

profiler start
# 一段时间(>30s)后
profiler stop
exit
wget -O /usr/local/bin/toshelper http://tosv.byted.org/obj/ee-oi-intranet/toshelper && chmod +x /usr/local/bin/toshelper
toshelper /tmp/XXX.svg

查找类、查看类详细信息与反编译代码

参考

# 可以找到需要的类全路径
# -d 输出当前类的详细信息:所加载的原始文件来源、类的声明、
# 加载的ClassLoader(可以看出是哪个组件加载的)等详细信息。
sc -d *DispatcharServlet      

# 查看某个方法的信息,如果存在的话 ,不加方法的话查看所有方法
sm org.springframework.web.servlet.DispatcherServlet getHandler 

# 直接反编译出java 源代码,包含类加载器等额外信息
jad package.XXXService.method 

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
sc -d查看详细类的详细信息
在这里插入图片描述

查看类中的所有field值

sc -d -f class

thread的各种操作

https://blog.csdn.net/u013735734/article/details/102930307?utm_source=app&app_version=4.13.0&code=app_1562916241&uLinkId=usr1mkqgl919blen
4.2节
查看topn的线程 + 具体线程堆栈 + 查看blocking线程、死锁线程 + 查看线程池等实例。

https://arthas.aliyun.com/doc/thread.html

// 查看所有线程信息
thread
// 查看前N线程
thread -n 3

// 查看具体线程
thread id
// 找出当前阻塞其他线程的线程
thread -b
// 查看指定状态的线程
thread --state WAITING

查看入参、返回值、异常

https://arthas.aliyun.com/doc/watch.html
支持复杂的设定

# 同时监控入参,返回值,及异常。如果有异常,直接打印出来
watch package.testMethod "{params, returnObj, throwExp}" -e -x 2  

几个watch结合ognl的实例

# 观察CommonTest的test方法
# 输出 入参、返回结果、抛出的异常 —— 输出的内容可以动态调整
# 后面跟着的是 条件表达式,表示耗时超过10ms才输出
# -n 表示只执行一次,-x表示 入参和返回结果的展开层次为5层
watch *.CommonTest test "{params,returnObj,throwExp}" '#cost>10' -x 5 -n 1

# 耗时大于10ms并且第一个参数等于1才输出
watch *.CommonTest test "{params,returnObj,throwExp}" '#cost>10 && params[0]==1' -x 5 -n 1

# 第一个参数大于1 并且第二个参数等于hello才输出
watch *.CommonTest test "{params,returnObj,throwExp}" 'params[0]>1 && params[1]=="hello"' -x 5 -n 1
# 第一个参数小于5或者第二个参数等于"world"就输出
watch *.CommonTest test "{params,returnObj,throwExp}" 'params[0]<5 || params[1]=="wolrd"' -x 5 -n 1
# 第一个参数的name字段等于world时才输出。
# 由于在方法执行过程中参数的name属性可能发生改变,因此加上-b才能观察到真正的入参
watch -b *.CommonTest test "{params,returnObj,throwExp}" 'params[0].name=="wolrd"' -x 5 -n 1

# 由于同时指定了-s和-b,所以方法被调用一次,就会输出2次结果(两个场景分开输出),分别是方法被调用前,和返回之后
# 注意,这里如果-n只设置成1,那么只会输出-b对应的输出,-s对应的输出由于没有次数了就无法输出了
watch *.CommonTest test '{params,returnObj,throwExp}' -x 5 -n 2 -s -b

线上修改代码:jad/mc/redefine/retransform

整体而言,还是使用Spring提供的hot swap更为合适。
https://github.com/alibaba/arthas/issues/537

Arthas: https://github.com/alibaba/arthas
jad命令:https://alibaba.github.io/arthas/jad.html
mc命令:https://alibaba.github.io/arthas/mc.html
redefine命令:https://alibaba.github.io/arthas/redefine.html
retransform:https://arthas.aliyun.com/doc/retransform.html

# 先反编译出class源码
jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java  

# 然后使用外部工具编辑内容
vim /tmp/UserController.java

# 查找加载UserController的ClassLoader
sc -d *UserController | grep classLoaderHash
 #output : classLoaderHash   1be6f5c3

 # 再编译成class,只需指明目录即可
 # 有时候mc命令不一定成功,尤其是很复杂的类有着各种注入各种依赖。
mc -c 1be6f5c3 /tmp/UserController.java -d /tmp 

 # 最后,重新载入定义的类
redefine /tmp/com/example/demo/arthas/user/UserController.class
retransform /tmp/Test.class

更好的做法:
直接在IDE上完成class的编辑,然后去生产环境中将代码拷贝进去,运行redefine命令即可。
https://blog.csdn.net/javageektech/article/details/101443191
注意:

  1. redefine的class不能修改、添加、删除类的field和method,包括方法参数、方法名称及返回值
  2. 如果mc失败,可以在本地开发环境编译好class文件,上传到目标系统,使用redefine热加载class
  3. 目前redefine 和watch/trace/jad/tt等命令冲突,
  4. redefine后的原来的类不能恢复,redefine有可能失败

retransform直接替换jvm里已经加载的class方法。好处是可以恢复。
上传 .class 文件到服务器的技巧

性能监控

monitor:方法执行结果监控,统计重要信息。https://alibaba.github.io/arthas/monitor.html

$ monitor -c 5 demo.MathGame primeFactors
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 94 ms.
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:06:38  demo.MathGame  primeFactors  5      1        4     1.15        80.00%
 
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:06:43  demo.MathGame  primeFactors  5      3        2     42.29       40.00%
 
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:06:48  demo.MathGame  primeFactors  5      3        2     67.92       40.00%
 
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:06:53  demo.MathGame  primeFactors  5      2        3     0.25        60.00%
 
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:06:58  demo.MathGame  primeFactors  1      1        0     0.45        0.00%
 
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:07:03  demo.MathGame  primeFactors  2      2        0     3182.72     0.00%

tt:时空隧道,记录了过程中的快照,方便Arthas内部线程重新发起一次调用。https://arthas.aliyun.com/doc/tt.html

// 留下快照
$ tt -l
 INDEX   TIMESTAMP            COST(ms)  IS-RET  IS-EXP   OBJECT         CLASS                          METHOD
-------------------------------------------------------------------------------------------------------------------------------------
 1000    2018-12-04 11:15:38  1.096236  false   true     0x4b67cf4d     MathGame                       primeFactors
 1001    2018-12-04 11:15:39  0.191848  false   true     0x4b67cf4d     MathGame                       primeFactors
 1002    2018-12-04 11:15:40  0.069523  false   true     0x4b67cf4d     MathGame                       primeFactors
 1003    2018-12-04 11:15:41  0.186073  false   true     0x4b67cf4d     MathGame                       primeFactors
 1004    2018-12-04 11:15:42  17.76437  true    false    0x4b67cf4d     MathGame                       primeFactors
                              9
 1005    2018-12-04 11:15:43  0.4776    false   true     0x4b67cf4d     MathGame                       primeFactors
Affect(row-cnt:6) cost in 4 ms.
// 详细信息查询
$ tt -i 1003
 INDEX            1003
 GMT-CREATE       2018-12-04 11:15:41
 COST(ms)         0.186073
 OBJECT           0x4b67cf4d
 CLASS            demo.MathGame
 METHOD           primeFactors
 IS-RETURN        false
 IS-EXCEPTION     true
 PARAMETERS[0]    @Integer[-564322413]
 THROW-EXCEPTION  java.lang.IllegalArgumentException: number is: -564322413, need >= 2
                      at demo.MathGame.primeFactors(MathGame.java:46)
                      at demo.MathGame.run(MathGame.java:24)
                      at demo.MathGame.main(MathGame.java:16)
 
Affect(row-cnt:1) cost in 11 ms.

发起执行

[arthas@10718]$ tt -t demo.MathGame run -n 5
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 56 ms, listenerId: 1
 INDEX      TIMESTAMP                   COST(ms)     IS-RET     IS-EXP      OBJECT              CLASS                                     METHOD
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1000       2021-01-08 21:54:17         0.901091     true       false       0x7699a589          MathGame                                  run
[arthas@10718]$ tt -w '@demo.MathGame@random.nextInt(100)'  -x 1 -i 1000
@Integer[46]

trace:单纯时间统计https://arthas.aliyun.com/doc/trace.html

# 输出方法内部调用路径,并输出方法路径上的每个节点上耗时,根据耗时进行过滤
trace demo.MathGame run '#cost > 10' 

dump

dump:dump 已加载类的 bytecode 到特定目录. https://arthas.aliyun.com/doc/dump.html
heapdump:dump java heap, 类似jmap命令的heap dump功能。https://arthas.aliyun.com/doc/heapdump.html

heapdump --live /tmp/jvm.hprof

# 之后使用tos回传
wget -O /usr/local/bin/toshelper http://tosv.byted.org/obj/ee-oi-intranet/toshelper && chmod +x /usr/local/bin/toshelper
toshelper /opt/tiger/multi-tenant-custom-entity/multi-tenant-custom-entity-cos-web/arthas-output/20210720-102201.svg

找到异常线程并查看堆栈

thread命令参看文章前半截
https://github.com/alibaba/arthas/issues/1202

方法一:
# 查找进程
top
# 查看进程线程
top -Hp 进程号
#或者也可以采用下面2种ps命令来查看繁忙的线程信息
ps -mp pid -o THREAD,tid,time
ps -Lfp pid 
# 16进制线程号
printf "%x\n" 目标线程号 
# jstack查看线程堆栈,jstack会打印出所有线程堆栈
jstack 进程号 | grep 线程ID

方法二:
# show busy threads的方法:直接打印出异常线程堆栈
wget --no-check-certificate https://raw.github.com/oldratlee/useful-scripts/release-2.x/bin/show-busy-java-threads
chmod +x show-busy-java-threads
 ./show-busy-java-threads

方法三:
# Arthas方法,查找cpu占用量最高的线程
thread -n 10
# 查看堆栈
thread pid

方法四:
# jstack 、top的文件也可以使用这个网站来分析
https://fastthread.io/ft-index.jsp

Mbean

命令介绍:https://arthas.aliyun.com/doc/mbean.html
什么是Mbean与jmx:
https://juejin.cn/post/6856949531003748365
https://www.liaoxuefeng.com/wiki/1252599548343744/1282385687609378
一种JVM运行时的监控框架。

案例总结

https://github.com/alibaba/arthas/issues?page=1&q=label%3Auser-case

trace查看耗时进行调优

https://github.com/alibaba/arthas/issues/1892
使用trace查看调用链路,从中分析耗时严重的函数。

Arthas底层实现

ByteKit解读:
https://github.com/alibaba/arthas/issues/1310
https://github.com/alibaba/arthas/issues/1311

WeakHashMap死循环引起CPU跑满

https://github.com/alibaba/arthas/issues/1709

thread + sc的应用
1,异常线程堆栈章节查到线程并且发现是weakHashMap的get方法出了问题
2,sc -d 从code-source中查看jar包,发现这个jar包里用到的是线程不安全版本的weakHashMap,遂破案。

JDK函数调用查询与后台脚本打印数据

案例:System.gc查找
https://github.com/alibaba/arthas/issues/20

stack:https://arthas.aliyun.com/doc/stack.html

实时观察版本:

# Arthas默认关闭了对JDK类的自带类的增强,需要通过options命令打开。
options unsafe true 
# stack命令:输出当前方法被调用的调用路径,实时观察谁调用了java.lang.System#gc,等待触发
# 实际上是不带每一步调用时间信息的trace版本
stack java.lang.System gc

后台打印版本

# 编辑脚本
cat system_gc.as

# 脚本内容
options unsafe true
# 务必注意这里的-n,表示这个命令只执行一次,然后退出。如果不指定,Arthas 2.0会一直卡在那里,永远不退出,Arthas 3.0默认执行100次才会退出。在写批处理脚本的时候,请务必加上-n参数。
stack java.lang.System gc -n 1

# 后台执行脚本1
./as.sh -b -f system_gc.as 59863 > system_gc.out 2>&1 &
# 后台执行脚本2,这种方式能够避免session因为连接超时中断的同时导致该脚本中断
nohup ./as.sh -b -f system_gc.as 59863 > system_gc.out 2>&1 &
# 后代执行脚本3,使用screen避免脚本中断
sudo yum install -b current screen
screen
./as.sh -b -f system_gc.as 59863 > system_gc.out 2>&1
# 即便网络断掉也没有问题。过一段时间之后,要查看之前的结果。如果连接断掉了,只需要输入以下命令
screen -r

#过段时间观察即可
cat system_gc.out

ognl表达式筛选并观察输入输出

ognl表达式:https://github.com/alibaba/arthas/issues/11
一些例子:https://github.com/alibaba/arthas/issues/71

# 打印线上某个容器的size
ognl '@xxx.common.redis.collections.UniqConcurrentSet@INSTANCE.dataIsNullSet.size()
# 在内部类某个方法中查看外部类容器大小
# https://github.com/alibaba/arthas/issues/772
watch sun.net.httpserver.ServerImpl$ServerTimerTask1 run -f "target.this$0.allConnections.size()

查看某个Spring引用的具体实现

日志打满问题解决:https://github.com/alibaba/arthas/issues/237

# -d显示详细信息
sc -d *LarkService*
# 查看具体实现类
getstatic -c 73ad2d6 io.netty.channel.nio.NioEventLoop logger 'getClass().getName()'
getstatic -c 73ad2d6 io.netty.channel.nio.NioEventLoop logger 'logger'
getstatic -c 73ad2d6 io.netty.channel.nio.NioEventLoop logger 'logger.getClass().getProtectionDomain().getCodeSource().getLocation()'
field: logger
# 查询其基类的积累的logger实现
getstatic -c 73ad2d6 io.netty.channel.nio.NioEventLoop logger 'logger.parent.parent.parent.parent.parent'
field: logger

tt获取Spring容器内的bean

https://github.com/alibaba/arthas/issues/482
时空隧道中能抓取到bean的调用
实际上用的就是idea插件生成的ognl语句。

list出现环导致死循环引发的CPU负载过高

https://github.com/alibaba/arthas/issues/1202

thread
thread pid

# 查看方法调用情况
tt -t com.google.common.collect.HashBiMap seekByKey -n 100

# SpringMVC应用,所有请求都会被RequestMappingHandlerAdapter拦截,我们通过tt命令,监听invokeHandlerMethod的执行,然后在页面随便点点,就会得到以下内容
[arthas@384]$ tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod -n 10
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 622 ms.

 INDEX	COST(ms)  	OBJECT    	CLASS             METHOD                                             
------------------------------------------------------------------------------------
 1000 	481.203383	0x481eb705	RequestMappingHandlerAdapter    invokeHandlerMethod                                
 1001 	3.432024  	0x481eb705	RequestMappingHandlerAdapter	invokeHandlerMethod                               
...
# tt 命令会记录方法调用时的所有入参和返回值、抛出的异常、对象本身等数据。通过 -i 参数后边跟着对应的 INDEX 编号查看这条记录的详细信息。再通过-w参数,指定一个OGNL表达式,查找相关对象
# Arthas会把当前执行的对象放到target变量中,通过target.getApplicationContext()就得到了SpringContext对象,这里同上一节内容
# OGNL写一个函数,来实现链表的环路检测,在OGNL里写一段环路检测代码里是不太容易的,一个bucket不太可能有50个以上的节点,所以就通过遍历次数是否大于50来判断是否有环路。
tt -i 1000 -w 'target.getApplicationContext().getBean("oaInfoManager").userCache.entrySet().{delegate}.{^ #loopCnt = 0,#foundCycle = :[ #this == null ? false : #loopCnt > 50 ? true : (#loopCnt = #loopCnt + 1, #foundCycle(#this.nextInKToVBucket))], #foundCycle(#this)}.get(0)' -x 2

奇怪日志来源定位

https://github.com/alibaba/arthas/issues/263
实际上是通过重写StringBuilder代码来定位某个奇怪日志的stack
使用到的技术:
redefine

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值