Arthas
是Alibaba开源的Java诊断工具,深受开发者喜爱,是线上问题的排除的利器。下面我们对Arthas做个简单介绍。
解决什么问题?
Arthas能解决很多问题,今天主要说下我在项目最常解决的问题,下面先列出问题,感兴趣的小伙伴可以继续往下看;不感兴趣的,可以出门右转再去看看其他同学的文章。
问题一:修改的代码有没有发布上去,好像没起作用嘛
问题二:线上报了一个异常,只能加日志发布上线才能排除,太麻烦了
问题三:有个接口,平时运行正常,压测的时候,到一定并发就慢下来了,不知道什么原因
今天就看看这三个问题通过Arthas怎么定位问题。看问题前,我们还得先把工具装起来。
Arthas安装
安装前,先搞个官方DEMO跑起来(项目代码就不搞了,免得。。。大家都懂得)
curl-O https://arthas.aliyun.com/arthas-demo.jar java -jar arthas-demo.jar
最简安装方式,也是官方推荐的方式(前提得能访问公网):
#下载curl-O https://arthas.aliyun.com/arthas-boot.jar #启动java -jar arthas-boot.jar
其他安装方式可以看官方文档,道路千万条。
启动后,会要选择应用java进程
$ $ java-jar arthas-boot.jar * [1]: 35542 [2]: 71560 arthas-demo.jar
选择第二个demo进程,控制台输入2,再输入回车/enter
。Arthas会attach到目标进程上,并输出日志:
[INFO] Try to attach process 71560[INFO] Attach process 71560 success. [INFO] arthas-client connect 127.0.0.1 3658 ,---. ,------. ,--------.,--. ,--. ,---. ,---. / O \ | .--. ''--. .--'| '--' | / O \ ' .-'| .-. || '--'.' | | | .--. || .-. |`. `-. | | | || |\ \ | | | | | || | | |.-' |`--'`--'`--''--' `--' `--'`--'`--' `--'`-----'wiki: https://arthas.aliyun.com/doc version: 3.0.5.20181127201536 pid: 71560time: 2018-11-2819:16:24 $
后面就可以干活了,先来个大盘看看:
输入dashboard,按回车/enter
,会展示当前进程的信息,按ctrl+c
可以中断执行。
$ dashboardID NAME GROUP PRIORI STATE %CPU TIME INTERRU DAEMON 17 pool-2-thread-1 system 5 WAITIN 670:0 falsefalse27 Timer-for-arthas-dashb system 10 RUNNAB 320:0 falsetrue11 AsyncAppender-Worker-a system 9 WAITIN 00:0 falsetrue9 Attach Listener system 9 RUNNAB 00:0 falsetrue3 Finalizer system 8 WAITIN 00:0 falsetrue2 Reference Handler system 10 WAITIN 00:0 falsetrue4 Signal Dispatcher system 9 RUNNAB 00:0 falsetrue26 as-command-execute-dae system 10 TIMED_ 00:0 falsetrue13 job-timeout system 9 TIMED_ 00:0 falsetrue1 main main 5 TIMED_ 00:0 falsefalse14 nioEventLoopGroup-2-1 system 10 RUNNAB 00:0 falsefalse18 nioEventLoopGroup-2-2 system 10 RUNNAB 00:0 falsefalse23 nioEventLoopGroup-2-3 system 10 RUNNAB 00:0 falsefalse15 nioEventLoopGroup-3-1 system 10 RUNNAB 00:0 falsefalseMemory used total max usage GC heap 32M 155M 1820M 1.77% gc.ps_scavenge.count 4ps_eden_space 14M 65M 672M 2.21% gc.ps_scavenge.time(m 166ps_survivor_space 4M 5M 5M s) ps_old_gen 12M 85M 1365M 0.91% gc.ps_marksweep.count 0nonheap 20M 23M -1 gc.ps_marksweep.time( 0code_cache 3M 5M 240M 1.32% ms) Runtime os.name Mac OS X os.version 10.13.4 java.version 1.8.0_162 java.home /Library/Java/JavaVir tualMachines/jdk1.8.0 _162.jdk/Contents/Hom e/jre
具体指标含义就不讲了,不然跑题了,都是jvm的基础知识了,该回到我们的正题了。
问题一
修改的代码有没有发布上去,好像没起作用嘛
这个问题对于Arthas来说太简单了,一个命令搞定:
jaddemo.MathGameClassLoader: +-sun.misc.Launcher$AppClassLoader+-sun.misc.Launcher$ExtClassLoaderLocation: /tmp/arthas-demo.jar/** Decompiled with CFR 0_132.*/packagedemo; importjava.io.PrintStream; importjava.util.ArrayList; importjava.util.Iterator; importjava.util.List; importjava.util.Random; importjava.util.concurrent.TimeUnit; publicclassMathGame { privatestaticRandomrandom=newRandom(); privateintillegalArgumentCount=0; publicstaticvoidmain(String[] args) throwsInterruptedException { MathGamegame=newMathGame(); do { game.run(); TimeUnit.SECONDS.sleep(1L); } while (true); } publicvoidrun() throwsInterruptedException { try { intnumber=random.nextInt(); List<Integer>primeFactors=this.primeFactors(number); MathGame.print(number, primeFactors); } catch (Exceptione) { System.out.println(String.format("illegalArgumentCount:%3d, ", this.illegalArgumentCount) +e.getMessage()); } } publicstaticvoidprint(intnumber, List<Integer>primeFactors) { StringBuffersb=newStringBuffer(""+number+"="); Iterator<Integer>iterator=primeFactors.iterator(); while (iterator.hasNext()) { intfactor=iterator.next(); sb.append(factor).append('*'); } if (sb.charAt(sb.length() -1) =='*') { sb.deleteCharAt(sb.length() -1); } System.out.println(sb); } publicList<Integer>primeFactors(intnumber) { if (number<2) { ++this.illegalArgumentCount; thrownewIllegalArgumentException("number is: "+number+", need >= 2"); } ArrayList<Integer>result=newArrayList<Integer>(); inti=2; while (i<=number) { if (number%i==0) { result.add(i); number/=i; i=2; continue; } ++i; } returnresult; } } Affect(row-cnt:1) costin970ms.
直接线上反编译一下,代码一目了然,是不是很简单。
问题二
线上报了一个异常,只能加日志发布上线才能排除,太麻烦了
这个问题,大家应该经常遇到吧,自己测试好好的,在线上报错,你说气人不!这种问题最常见的就是方法的入参或中间调用其他服务返回值不符合预期又没有做容错判断导致的,Arthas有一个命令可以解决。
watch命令:让你能方便的观察到指定方法的调用情况。能观察到的范围为:返回值
、抛出异常
、入参
,通过编写 OGNL 表达式进行对应变量的查看,少码几个字,直接上代码:
$watchdemo.MathGameprimeFactors"{params,returnObj}"-x2PressCtrl+Ctoabort. Affect(class-cnt:1 , method-cnt:1) costin44ms. ts=2018-12-0319:16:51; [cost=1.280502ms] result= [ [][ 1], [ ], [ 3], [19], [191], [49199], [ ], ]
简单解释下(复杂解释就交给官方大佬们吧:),我只负责勾起大家的兴趣):
demo.MathGame 包名和类名
primeFactors 方法名
"{params,returnObj}" 观察表达式一个标准ONGL表达式,简单实用,就这样写就ok,负责的请移步官方文档
-x 2 指定输出结果的属性遍历深度,默认为 1,简单逻辑,你想看的参数值或返回值看不到,就把数字加大就ok了
通过监听输入输出,结合代码基本问题都能定位到了。
问题三
有个接口,平时运行正常,压测的时候,到一定并发就慢下来了,不知道什么原因
这个问题也是我们线上压测的时候遇到的,一个接口方法逻辑较复杂,平时调用速度还可以,30ms以内;压测到一定并发量,接口就明显慢下来了,分析了半天也没找到原因,最后寄出神奇Arthas,顺利定位问题,轻松解决,给大家多争取了两个小时睡觉时间,废话太多了,直接上代码:
$tracedemo.MathGamerunPressQorCtrl+Ctoabort. Affect(class-cnt:1 , method-cnt:1) costin28ms. `---ts=2019-12-0400:45:08;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader`---[0.617465ms] demo.MathGame:run() `---[0.078946ms] demo.MathGame:primeFactors() #24 [throwsException] `---ts=2019-12-0400:45:09;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader`---[1.276874ms] demo.MathGame:run() `---[0.03752ms] demo.MathGame:primeFactors() #24 [throwsException]
通过trace命令,方法中,每个调用的执行时间一目了然,还担心找不到哪慢吗?
ok,简单介绍就到这吧,再看下去估计大家就睡着了,不过还有一个重要注意和大家说一下。
注意事项
Arthas不要再系统高峰时使用,用完一定要退出!
Arthas不要再系统高峰时使用,用完一定要退出!
Arthas不要再系统高峰时使用,用完一定要退出!
重要的事情说三遍,我们实际压测对比过,相同服务节点安装和不安装Arthas,性能差异还是很明显的。
完全退出命令:stop