百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

Java线上问题排查神器Arthas快速上手与原理浅谈

toyiye 2024-06-27 00:53 23 浏览 0 评论

关注我的微信公众号:后端技术漫谈

不定期推送关于后端开发、爬虫、算法题、数据结构方面的原创技术文章,以及生活中的逸闻趣事。

我目前是一名后端开发工程师。主要关注后端开发,数据安全,网络爬虫,物联网,边缘计算等方向。

原创博客主要内容

  • Java知识点复习全手册
  • Leetcode算法题解析
  • 剑指offer算法题解析
  • SpringCloud菜鸟入门实战系列
  • SpringBoot菜鸟入门实战系列
  • Python爬虫相关技术文章
  • 后端开发相关技术文章

前言

当你兴冲冲地开始运行自己的Java项目时,你是否遇到过如下问题:

  • 程序在稳定运行了,可是实现的功能点了没反应。
  • 为了修复Bug而上线的新版本,上线后发现Bug依然在,却想不通哪里有问题?
  • 想到可能出现问题的地方,却发现那里没打日志,没法在运行中看到问题,只能加了日志输出重新打包——部署——上线
  • 程序功能正常了,可是为啥响应时间这么慢,在哪里出现了问题?
  • 程序不但稳定运行,而且功能完美,但跑了几天或者几周过后,发现响应速度变慢了,是不是内存泄漏了?

以前,你碰到这些问题,解决的办法大多是,修改代码,重新上线。但是在大公司里,上线的流程是非常繁琐的,如果为了多加一行日志而重新发布版本,无疑是非常折腾人的。

现在,我们有了更为优雅的线上调试方法,来自阿里巴巴开源的Arthas

下图是Arthas文档中对于为什么要使用它的描述,我进行了精简:

好了,前言已经超过字数了,哈哈,在本篇文章里,你能够了解:

  • Arthas使用实例:帮助你快速让你上手,拯救你的低效率Debug
  • 使用Arthas解决具体问题:看一下Arthas帮我拯救了多少时间
  • 相似工具:看看线上Debug还有没有别的工具可以使用
  • 原理浅谈:莫在浮沙筑高阁!你需要大概了解下Arthas的原理

相信我,Arhas觉得是你提升效率的利器,适合各种阶段的开发者,尤其适合我这种刚入门的新人(天天上班写Bug的人)。你不应该有这种东西是高阶程序员才应该去使用的思想,放心大胆的去用吧

(打广告时间,更多精彩文章,请关注公众号:后端技术漫谈

线上Debug神器Arthas

Arthas使用实例

命令的详细文档请参考:

https://alibaba.github.io/arthas/commands.html

快速启动

快速启动它,你只需要两行命令:

wget https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar

随后,在界面出现的进程中,选择你的程序序号,比如1

这样你就进入了arthas的控制台

基本使用

Arthas有如下功能:

1. 首先是我认为的“上帝视角”指令:Dashboard

当前系统的实时数据面板,按 ctrl+c 退出。

当运行在Ali-tomcat时,会显示当前tomcat的实时信息,如HTTP请求的qps, rt, 错误数, 线程池信息等等。

通过这些,你可以对于整个程序进程有个直观的数据监控。

2. 类加载问题相关指令

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.

3. 方法运行相关指令

watch:方法执行的数据观测

你可以通过watch指令,来监控某个类,监控后,运行下你的功能,复现下场景,arthas会提供给你具体的出参和入参,帮助你排查故障

trace:输出方法调用路径,并输出耗时

这个指令对于优化代码非常的有用,可以看出具体每个方法执行的时间,如果是for循环等重复语句,还能看出n次循环中的最大耗时,最小耗时,和平均耗时,完美!

tt:官方名为时空隧道

这是我调试用的最多的指令,在你对某方法开启tt后,会记录下每一次的调用(你需要设置最大监控次数),然后你可以在任何时候会看这里面的调用,包括出参,入参,运行耗时,是否异常等。非常强大。

4. 线程调试相关指令

thread相关命令:

thread -n:排列出 CPU 使用率 Top N 的线程。

thread -b:排查阻塞的线程

我们代码有时候设计的不好,会引发死锁的问题,卡住整个线程执行,使用这个指令可以轻松的找到问题线程,以及问题的执行语句。

  1. 强大的ognl表达式

众所周知,一般来说,表达式都是调试工具里最强的指令,哈哈。

在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],

]

使用Arthas解决具体问题

1. 响应时间异常问题

工作中遇到一个优化问题,系统中一个导出表格的功能,响应时间长达2分钟,虽然给内部使用,但也不能这么夸张,用trace跟踪下方法,发现是其中的手机号加解密函数占用了非常大的时间,几千个手机号,进行了解密后加密的精彩操作,最终导致了两分钟的返回时间。

2. 某功能Bug导致服务器返回500

首先通过trace看异常报错的方法,之后通过tt排查方法,发现入参进来后,居然走错了方法(因为多态),走到了返回null的方法中,所以导致了NPE空指针错误。

补充

Arthas还支持Web Console,详见:

https://alibaba.github.io/arthas/web-console.html

相似工具

BTrace一是个历史比较久的工具,观察下来Arthas其实和他的理念蛮相似的,相信Arthas也参考过Btrace,作为一个学习样例来开发Arthas。详细的优劣势看图:

其他的相似工具,还有jvm-sandbox,有兴趣的朋友可以去看看。

原理浅谈

分为三个部分:

  • 启动
  • arthas服务端代码分析
  • arthas客户端代码分析

启动

使用了阿里开源的组件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方法开始运行。

  • 首先解析入参,生成com.taobao.arthas.core.config.Configure类,包含了相关配置信息
  • 使用jdk-tools里面的VirtualMachine.loadAgent,其中第一个参数为agent路径, 第二个参数向jar包中的agentmain()方法传递参数(此处为agent-core.jar包路径和config序列化之后的字符串),加载arthas-agent.jar包
  • 运行arthas-agent.jar包,指定了Agent-Class为com.taobao.arthas.agent.AgentBootstrap

上图中代码:

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);
 }
}

服务端——监听客户端请求

  • 如果是exit,logout,quit,jobs,fg,bg,kill等直接执行。
  • 如果是其他的命令,则创建Job,并运行。
  • 创建Job时,会根据具体客户端传递的命令,找到对应的Command,并包装成Process, Process再被包装成Job。
  • 运行Job时,反向先调用Process,再找到对应的Command,最终调用Command的process处理请求。

服务端——Command处理流程

  1. 不需要使用字节码增强的命令

其中JVM相关的使用 java.lang.management 提供的管理接口,来查看具体的运行时数据。比较简单,就不介绍了。

  1. 需要使用字节码增强的命令

字节码增加的命令统一继承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连接,关键的代码有下面几步:

  1. 构造TelnetClient对象,并初始化
  2. 构造ConsoleReader对象,并初始化
  3. 调用IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(), System.in, consoleReader.getOutput())处理各个流,一共有四个流: telnet.getInputStream() telnet.getOutputStream() System.in consoleReader.getOutput()

请求时:从本地System.in读取,发送到 telnet.getOutputStream(),即发送给远程服务端。
响应时:从telnet.getInputStream()读取远程服务端发送过来的响应,并传递给 consoleReader.getOutput(),即在本地控制台输出。

关于源码,深入下去还有很多东西需要生啃,我也没有消化得很好,大家可以继续阅读详细资料。

总结

Arthas是一个线上Deubg神器,小白也可以轻松上手。

码字不易,希望大家捧个人场,谢谢诸位。

参考文献

开源地址:

https://github.com/alibaba/arthas

官方文档:

https://alibaba.github.io/arthas

其他参考:

  1. Hollis:Arthas - Java 线上问题定位处理的终极利器
  2. https://www.cnblogs.com/LittleHann/p/4783581.html
  3. https://juejin.im/post/5c466d7bf265da61511509ab#heading-29
  4. http://tech.dianwoda.com/2018/12/20/arthasyuan-ma-fen-xi/
  5. https://www.jianshu.com/p/0771646f3f25
  6. https://github.com/alibaba/arthas/blob/master/README_CN.md
  7. https://blog.csdn.net/qq_27376871/article/details/51613066
  8. https://github.com/alibaba/arthas/issues/222

关注我

我是一名后端开发工程师。主要关注后端开发,数据安全,网络爬虫,物联网,边缘计算等方向,欢迎交流。

各大平台都可以找到我

  • Github:@qqxx6661
  • CSDN:@Rude3Knife
  • 知乎:@Zhendong
  • 简书:@蛮三刀把刀
  • 掘金:@蛮三刀把刀

原创博客主要内容

  • Java知识点复习全手册
  • Leetcode算法题解析
  • 剑指offer算法题解析
  • SpringBoot菜鸟入门实战系列
  • SpringCloud菜鸟入门实战系列
  • 爬虫相关技术文章
  • 后端开发相关技术文章
  • 逸闻趣事/好书分享/个人兴趣

个人公众号:后端技术漫谈

公众号:后端技术漫游.jpg

如果文章对你有帮助,不妨收藏起来并转发给您的朋友们~

相关推荐

如何用 coco 数据集训练 Detectron2 模型?

随着最新的Pythorc1.3版本的发布,下一代完全重写了它以前的目标检测框架,新的目标检测框架被称为Detectron2。本教程将通过使用自定义coco数据集训练实例分割模型,帮助你开始使...

CICD联动阿里云容器服务Kubernetes实践之Bamboo篇

本文档以构建一个Java软件项目并部署到阿里云容器服务的Kubernetes集群为例说明如何使用Bamboo在阿里云Kubernetes服务上运行RemoteAgents并在agents上...

Open3D-ML点云语义分割实验【RandLA-Net】

作为点云Open3D-ML实验的一部分,我撰写了文章解释如何使用Tensorflow和PyTorch支持安装此库。为了测试安装,我解释了如何运行一个简单的Python脚本来可视化名为...

清理系统不用第三方工具(系统自带清理软件效果好不?)

清理优化系统一定要借助于优化工具吗?其实,手动优化系统也没有那么神秘,掌握了方法和技巧,系统清理也是一件简单和随心的事。一方面要为每一个可能产生累赘的文件找到清理的方法,另一方面要寻找能够提高工作效率...

【信创】联想开先终端开机不显示grub界面的修改方法

原文链接:【信创】联想开先终端开机不显示grub界面的修改方法...

如意玲珑成熟度再提升,三大发行版支持教程来啦!

前期,我们已分别发布如意玲珑在deepinV23与UOSV20、openEuler24.03发行版的操作指南,本文,我们将为大家详细介绍Ubuntu24.04、Debian12、op...

118种常见的多媒体文件格式(英文简写)

MP4[?mpi?f??]-MPEG-4Part14(MPEG-4第14部分)AVI[e?vi??a?]-AudioVideoInterleave(音视频交错)MOV[m...

密码丢了急上火?码住7种console密码紧急恢复方式!

身为攻城狮的你,...

CSGO丨CS2的cfg指令代码分享(csgo自己的cfg在哪里?config文件位置在哪?)

?...

使用open SSL生成局域网IP地址证书

某些特殊情况下,用户内网访问多可文档管理系统时需要启用SSL传输加密功能,但只有IP,没有域名和证书。这种情况下多可提供了一种免费可行的方式,通过openSSL生成免费证书。此方法生成证书浏览器会提示...

Python中加载配置文件(python怎么加载程序包)

我们在做开发的时候经常要使用配置文件,那么配置文件的加载就需要我们提前考虑,再不使用任何框架的情况下,我们通常会有两种解决办法:完整加载将所有配置信息一次性写入单一配置文件.部分加载将常用配置信息写...

python开发项目,不得不了解的.cfg配置文件

安装软件时,经常会见到后缀为.cfg、.ini的文件,一般我们不用管,只要不删就行。因为这些是程序安装、运行时需要用到的配置文件。但对开发者来说,这种文件是怎么回事就必须搞清了。本文从.cfg文件的创...

瑞芯微RK3568鸿蒙开发板OpenHarmony系统修改cfg文件权限方法

本文适用OpenHarmony开源鸿蒙系统,本次使用的是开源鸿蒙主板,搭载瑞芯微RK3568芯片。深圳触觉智能专注研发生产OpenHarmony开源鸿蒙硬件,包括核心板、开发板、嵌入式主板,工控整机等...

Python9:图像风格迁移-使用阿里的接口

先不多说,直接上结果图。#!/usr/bin/envpython#coding=utf-8importosfromaliyunsdkcore.clientimportAcsClient...

Python带你打造个性化的图片文字识别

我们的目标:从CSV文件读取用户的文件信息,并将文件名称修改为姓名格式的中文名称,进行规范资料整理,从而实现快速对多个文件进行重命名。最终效果:将原来无规律的文件名重命名为以姓名为名称的文件。技术点:...

取消回复欢迎 发表评论:

请填写验证码