【Arthas 官方社区正在举行征文活动,参加即有奖品拿哦~点击投稿】html
做者 | 杨桢栋,笔名叫蛮三刀把刀,是一名一线互联网码农,留美访学一年,主要关注后端开发,数据安全,爬虫,物联网,边缘计算等方向。java
当你兴冲冲地开始运行本身的 Java 项目时,你是否遇到过以下问题:mysql
之前,你碰到这些问题,解决的办法大可能是,修改代码,从新上线。可是在大公司里,上线的流程是很是繁琐的,若是为了多加一行日志而从新发布版本,无疑是很是折腾人的。git
如今,咱们有了更为优雅的线上调试方法 - 来自阿里巴巴开源的 Arthas。程序员
下图是 Arthas 文档中对于为何要使用它的描述,我进行了精简:github
好了,前言已经超过字数了,哈哈,在本篇文章里,你可以了解:web
相信我,Arhas 绝对是你提高效率的利器,适合各类阶段的开发者,尤为适合我这种刚入门的新人(每天上班写 Bug 的人)。你不该该有这种东西是高阶程序员才应该去使用的思想,放心大胆的去用吧!spring
命令的详细文档请参考:alibaba.github.io/arthas/comm…sql
快速启动它,你只须要两行命令:apache
wget https://alibaba.github.io/arthas/arthas-boot.jar java -jar arthas-boot.jar
随后,在界面出现的进程中,选择你的程序序号,好比 1
这样你就进入了 arthas 的控制台。
Arthas 有以下功能:
当前系统的实时数据面板,按 ctrl+c 退出;
当运行在 Ali-tomcat 时,会显示当前 tomcat 的实时信息,如 HTTP 请求的 qps, rt, 错误数, 线程池信息等等。
经过这些,你能够对于整个程序进程有个直观的数据监控。
SC:查看 JVM 已加载的类信息
经过 SC 咱们能够看到咱们这个类的详细信息,包括是从哪一个 jar 包读取的,他是否是接口/枚举类等,甚至包括他是从哪一个类加载器加载的。
上图中代码:
[arthas@37]$ sc -d *MathGame class-info demo.MathGame code-source /home/scrapbook/tutorial/arthas-demo.jar name demo.MathGame isInterface false isAnnotation false isEnum false isAnonymousClass false isArray false isLocalClass false isMemberClass false isPrimitive false isSynthetic false simple-name MathGame modifier public annotation interfaces super-class +-java.lang.Object class-loader +-sun.misc.Launcher$AppClassLoader@70dea4e +-sun.misc.Launcher$ExtClassLoader@69260973 classLoaderHash 70dea4e
SC 也能够查看已加载的类,帮助你看是否有没有归入进来的类,尤为是在 Spring 中,能够判断的你的依赖有没有正确的进来。
上图中代码:
# 查看JVM已加载的类信息 [arthas@37]$ sc javax.servlet.Filter com.example.demo.arthas.AdminFilterConfig$AdminFilter javax.servlet.Filter org.apache.tomcat.websocket.server.WsFilter org.springframework.boot.web.filter.OrderedCharacterEncodingFilter org.springframework.boot.web.filter.OrderedHiddenHttpMethodFilter org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter org.springframework.boot.web.filter.OrderedRequestContextFilter org.springframework.web.filter.CharacterEncodingFilter org.springframework.web.filter.GenericFilterBean org.springframework.web.filter.HiddenHttpMethodFilter org.springframework.web.filter.HttpPutFormContentFilter org.springframework.web.filter.OncePerRequestFilter org.springframework.web.filter.RequestContextFilter org.springframework.web.servlet.resource.ResourceUrlEncodingFilter Affect(row-cnt:14) cost in 11 ms. # 查看已加载类的方法信息 [arthas@37]$ sm java.math.RoundingMode java.math.RoundingMode <init>(Ljava/lang/String;II)V java.math.RoundingMode values()[Ljava/math/RoundingMode; java.math.RoundingMode valueOf(I)Ljava/math/RoundingMode; java.math.RoundingMode valueOf(Ljava/lang/String;)Ljava/math/RoundingMode; Affect(row-cnt:4) cost in 6 ms.
jad:反编译某个类,或者反编译某个类的某个方法。
上图中代码:
# 反编译只显示源码 jad --source-only com.Arthas # 反编译某个类的某个方法 jad --source-only com.Arthas mysql [arthas@37]$ jad demo.MathGame ClassLoader: +-sun.misc.Launcher$AppClassLoader@70dea4e +-sun.misc.Launcher$ExtClassLoader@69260973 Location: /home/scrapbook/tutorial/arthas-demo.jar /* * Decompiled with CFR. */ package demo; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.TimeUnit; public class MathGame { private static Random random = new Random(); public int illegalArgumentCount = 0; public List<Integer> primeFactors(int number) { if (number < 2) { ++this.illegalArgumentCount; throw new IllegalArgumentException("number is: " + number + ", need >= 2"); } ArrayList<Integer> result = new ArrayList<Integer>(); int i = 2; while (i <= number) { if (number % i == 0) { result.add(i); number /= i; i = 2; continue; } ++i; } return result; } public static void main(String[] args) throws InterruptedException { MathGame game = new MathGame(); do { game.run(); TimeUnit.SECONDS.sleep(1L); } while (true); } public void run() throws InterruptedException { try { int number = random.nextInt() / 10000; List<Integer> primeFactors = this.primeFactors(number); MathGame.print(number, primeFactors); } catch (Exception e) { System.out.println(String.format("illegalArgumentCount:%3d, ", this.illegalArgumentCount) + e.getMessage()); } } public static void print(int number, List<Integer> primeFactors) { StringBuffer sb = new StringBuffer(number + "="); for (int factor : primeFactors) { sb.append(factor).append('*'); } if (sb.charAt(sb.length() - 1) == '*') { sb.deleteCharAt(sb.length() - 1); } System.out.println(sb); } } Affect(row-cnt:1) cost in 760 ms.
watch:方法执行的数据观测
你能够经过 watch 指令,来监控某个类,监控后,运行下你的功能,复现下场景,arthas 会提供给你具体的出参和入参,帮助你排查故障。
trace:输出方法调用路径,并输出耗时
这个指令对于优化代码很是的有用,能够看出具体每一个方法执行的时间,若是是 for 循环等重复语句,还能看出 n 次循环中的最大耗时,最小耗时,和平均耗时,完美!
tt:官方名为时空隧道
这是我调试用的最多的指令,在你对某方法开启 tt 后,会记录下每一次的调用(你须要设置最大监控次数),而后你能够在任什么时候候会看这里面的调用,包括出参,入参,运行耗时,是否异常等。很是强大。
thread 相关命令:
thread -n:排列出 CPU 使用率 Top N 的线程。
thread -b:排查阻塞的线程
咱们代码有时候设计的很差,会引起死锁的问题,卡住整个线程执行,使用这个指令能够轻松的找到问题线程,以及问题的执行语句。
众所周知,通常来讲,表达式都是调试工具里最强的指令,哈哈。
在 Arthas 中你能够利用 ognl 表达式语言作不少事,好比执行某个方法,获取某个信息,甚至进行修改。
[arthas@19856]$ ognl '@com.Arthas@hashSet' @HashSet[ @String[count1], @String[count2], @String[count29], @String[count28], @String[count0], @String[count27], @String[count5], @String[count26], @String[count6], @String[count25], @String[count3], @String[count24], [arthas@19856]$ ognl '@com.Arthas@hashSet.add("test")' @Boolean[true] [arthas@19856]$ # 查看添加的字符 [arthas@19856]$ ognl '@com.Arthas@hashSet' | grep test @String[test], [arthas@19856]$
甚至你能够动态更换日志输出级别。
$ ognl '@com.lz.test@LOGGER.logger.privateConfig' @PrivateConfig[ loggerConfig=@LoggerConfig[root], loggerConfigLevel=@Level[INFO], intLevel=@Integer[400], ] $ ognl '@com.lz.test@LOGGER.logger.setLevel(@org.apache.logging.log4j.Level@ERROR)' null $ ognl '@com.lz.test@LOGGER.logger.privateConfig' @PrivateConfig[ loggerConfig=@LoggerConfig[root], loggerConfigLevel=@Level[ERROR], intLevel=@Integer[200], ]
工做中遇到一个优化问题,系统中一个导出表格的功能,响应时间长达 2 分钟,虽然给内部使用,但也不能这么夸张,用 trace 跟踪下方法,发现是其中的手机号加解密函数占用了很是大的时间,几千个手机号,进行了解密后加密的精彩操做,最终致使了两分钟的返回时间。
首先经过 trace 看异常报错的方法,以后经过 tt 排查方法,发现入参进来后,竟然走错了方法(由于多态),走到了返回 null 的方法中,因此致使了 NPE 空指针错误。
Arthas 还支持 Web Console,详见:alibaba.github.io/arthas/web-…
BTrace 一是个历史比较久的工具,观察下来 Arthas 其实和它的理念蛮类似的,相信 Arthas 也参考过 Btrace,做为一个学习样例来开发 Arthas。详细的优劣势看图:
其余的类似工具,还有 jvm-sandbox,有兴趣的朋友能够去看看。
分为三个部分:
使用了阿里开源的组件 cli,对参数进行了解析:com.taobao.arthas.boot.Bootstrap
在传入参数中没有 pid,则会调用本地 jps 命令,列出 java 进程。
进入主逻辑,会在用户目录下创建 .arthas 目录,同时下载 arthas-core 和 arthas-agent 等 lib 文件,最后启动客户端和服务端。
经过反射的方式来启动字符客户端。
看服务端启动命令能够知道 从 arthas-core.jar开始启动,arthas-core 的 pom.xml 文件里面指定了 mainClass 为 com.taobao.arthas.core.Arthas,使得程序启动的时候从该类的 main 方法开始运行。
上图中代码:
public class Arthas { private Arthas(String[] args) throws Exception { attachAgent(parse(args)); } private Configure parse(String[] args) { // 省略非关键代码,解析启动参数做为配置,并填充到configure对象里面 return configure; } private void attachAgent(Configure configure) throws Exception { // 省略非关键代码,attach到目标进程 virtualMachine = VirtualMachine.attach("" + configure.getJavaPid()); virtualMachine.loadAgent(configure.getArthasAgent(), configure.getArthasCore() + ";" + configure.toString()); } public static void main(String[] args) { new Arthas(args); } }
其中 JVM 相关的使用 java.lang.management 提供的管理接口,来查看具体的运行时数据。比较简单,就不介绍了。
字节码增长的命令统一继承 EnhancerCommand 类,process 方法里面调用 enhance 方法进行加强。调用 Enhancer 类 enhance 方法,该方法内部调用 inst.addTransformer 方法添加自定义的 ClassFileTransformer,这边是 Enhancer 类。Enhancer 类使用 AdviceWeaver(继承 ClassVisitor),用来修改类的字节码。重写了 visitMethod 方法,在该方法里面修改类指定的方法。visitMethod 方法里面使用了 AdviceAdapter(继承了 MethodVisitor类),在 onMethodEnter 方法, onMethodExit 方法中,把 Spy 类对应的方法(ON_BEFORE_METHOD, ON_RETURN_METHOD, ON_THROWS_METHOD 等)编织到目标类的方法对应的位置。
在前面 Spy 初始化的时候能够看到,这几个方法其实指向的是 AdviceWeaver 类的 methodOnBegin, methodOnReturnEnd 等。在这些方法里面都会根据 adviceId 查找对应的 AdviceListener,并调用 AdviceListener 的对应的方法,好比 before,afterReturning, afterThrowing。
客户端代码在 arthas-client 模块里面,入口类是 com.taobao.arthas.client.TelnetConsole。
主要使用 apache commons-net jar 进行 telnet 链接,关键的代码有下面几步:
调用 IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(), System.in, consoleReader.getOutput()) 处理各个流,一共有四个流:
请求时:从本地 System.in 读取,发送到 telnet.getOutputStream(),即发送给远程服务端。 响应时:从 telnet.getInputStream() 读取远程服务端发送过来的响应,并传递给 consoleReader.getOutput(),即在本地控制台输出。
关于源码,深刻下去还有不少东西须要生啃,我也没有消化得很好,你们能够继续阅读详细资料。
Arthas 是一个线上 Debug 神器,小白也能够轻松上手。
Cloud Toolkit 是阿里云发布的免费本地 IDE 插件,帮助开发者更高效地开发、测试、诊断并部署应用。经过插件,能够将本地应用一键部署到任意服务器,甚至云端(ECS、EDAS、ACK、ACR 和 小程序云等);而且还内置了 Arthas 诊断、Dubbo工具、Terminal 终端、文件上传、函数计算 和 MySQL 执行器等工具。不只仅有 IntelliJ IDEA 主流版本,还有 Eclipse、Pycharm、Maven 等其余版本。
推荐使用 IDEA 插件下载 Cloud Toolkit 来使用 Arthas:http://t.tb.cn/2A5CbHWveOXzI7sFakaCw8
地址:https://github.com/alibaba/arthas。
开源地址:
github.com/alibaba/art…
官方文档:
alibaba.github.io/arthas
Arthas 官方正在举行征文活动,若是你有:
欢迎参加征文活动,还有奖品拿哦~点击投稿
“ 阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,作最懂云原生开发者的公众号。”