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

2023-11-23 01:10

本文主要是介绍Arthas(阿尔萨斯)定位线上问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Arthas 阿尔萨斯使用

  • 参考
  • 实际使用案例
    • 线上安装与挂载
    • 火焰图
    • 查找类、查看类详细信息与反编译代码
    • 查看类中的所有field值
    • thread的各种操作
    • 查看入参、返回值、异常
    • 线上修改代码:jad/mc/redefine/retransform
    • 性能监控
    • dump
    • 找到异常线程并查看堆栈
    • Mbean
  • 案例总结
    • trace查看耗时进行调优
    • Arthas底层实现
    • WeakHashMap死循环引起CPU跑满
    • JDK函数调用查询与后台脚本打印数据
    • ognl表达式筛选并观察输入输出
    • 查看某个Spring引用的具体实现
    • tt获取Spring容器内的bean
    • list出现环导致死循环引发的CPU负载过高
    • 奇怪日志来源定位

参考

官方文档: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 -lINDEX   TIMESTAMP            COST(ms)  IS-RET  IS-EXP   OBJECT         CLASS                          METHOD
-------------------------------------------------------------------------------------------------------------------------------------1000    2018-12-04 11:15:38  1.096236  false   true     0x4b67cf4d     MathGame                       primeFactors1001    2018-12-04 11:15:39  0.191848  false   true     0x4b67cf4d     MathGame                       primeFactors1002    2018-12-04 11:15:40  0.069523  false   true     0x4b67cf4d     MathGame                       primeFactors1003    2018-12-04 11:15:41  0.186073  false   true     0x4b67cf4d     MathGame                       primeFactors1004    2018-12-04 11:15:42  17.76437  true    false    0x4b67cf4d     MathGame                       primeFactors91005    2018-12-04 11:15:43  0.4776    false   true     0x4b67cf4d     MathGame                       primeFactors
Affect(row-cnt:6) cost in 4 ms.
// 详细信息查询
$ tt -i 1003INDEX            1003GMT-CREATE       2018-12-04 11:15:41COST(ms)         0.186073OBJECT           0x4b67cf4dCLASS            demo.MathGameMETHOD           primeFactorsIS-RETURN        falseIS-EXCEPTION     truePARAMETERS[0]    @Integer[-564322413]THROW-EXCEPTION  java.lang.IllegalArgumentException: number is: -564322413, need >= 2at 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: 1INDEX      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

这篇关于Arthas(阿尔萨斯)定位线上问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/414240

相关文章

MybatisGenerator文件生成不出对应文件的问题

《MybatisGenerator文件生成不出对应文件的问题》本文介绍了使用MybatisGenerator生成文件时遇到的问题及解决方法,主要步骤包括检查目标表是否存在、是否能连接到数据库、配置生成... 目录MyBATisGenerator 文件生成不出对应文件先在项目结构里引入“targetProje

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

Java内存泄漏问题的排查、优化与最佳实践

《Java内存泄漏问题的排查、优化与最佳实践》在Java开发中,内存泄漏是一个常见且令人头疼的问题,内存泄漏指的是程序在运行过程中,已经不再使用的对象没有被及时释放,从而导致内存占用不断增加,最终... 目录引言1. 什么是内存泄漏?常见的内存泄漏情况2. 如何排查 Java 中的内存泄漏?2.1 使用 J

numpy求解线性代数相关问题

《numpy求解线性代数相关问题》本文主要介绍了numpy求解线性代数相关问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 在numpy中有numpy.array类型和numpy.mat类型,前者是数组类型,后者是矩阵类型。数组

解决systemctl reload nginx重启Nginx服务报错:Job for nginx.service invalid问题

《解决systemctlreloadnginx重启Nginx服务报错:Jobfornginx.serviceinvalid问题》文章描述了通过`systemctlstatusnginx.se... 目录systemctl reload nginx重启Nginx服务报错:Job for nginx.javas

Redis缓存问题与缓存更新机制详解

《Redis缓存问题与缓存更新机制详解》本文主要介绍了缓存问题及其解决方案,包括缓存穿透、缓存击穿、缓存雪崩等问题的成因以及相应的预防和解决方法,同时,还详细探讨了缓存更新机制,包括不同情况下的缓存更... 目录一、缓存问题1.1 缓存穿透1.1.1 问题来源1.1.2 解决方案1.2 缓存击穿1.2.1

vue解决子组件样式覆盖问题scoped deep

《vue解决子组件样式覆盖问题scopeddeep》文章主要介绍了在Vue项目中处理全局样式和局部样式的方法,包括使用scoped属性和深度选择器(/deep/)来覆盖子组件的样式,作者建议所有组件... 目录前言scoped分析deep分析使用总结所有组件必须加scoped父组件覆盖子组件使用deep前言

解决Cron定时任务中Pytest脚本无法发送邮件的问题

《解决Cron定时任务中Pytest脚本无法发送邮件的问题》文章探讨解决在Cron定时任务中运行Pytest脚本时邮件发送失败的问题,先优化环境变量,再检查Pytest邮件配置,接着配置文件确保SMT... 目录引言1. 环境变量优化:确保Cron任务可以正确执行解决方案:1.1. 创建一个脚本1.2. 修

Python 标准库time时间的访问和转换问题小结

《Python标准库time时间的访问和转换问题小结》time模块为Python提供了处理时间和日期的多种功能,适用于多种与时间相关的场景,包括获取当前时间、格式化时间、暂停程序执行、计算程序运行时... 目录模块介绍使用场景主要类主要函数 - time()- sleep()- localtime()- g

SpringBoot项目删除Bean或者不加载Bean的问题解决

《SpringBoot项目删除Bean或者不加载Bean的问题解决》文章介绍了在SpringBoot项目中如何使用@ComponentScan注解和自定义过滤器实现不加载某些Bean的方法,本文通过实... 使用@ComponentScan注解中的@ComponentScan.Filter标记不加载。@C