Arthas阿里开源的 Java 诊断工具

image-20210906221928133

Arthas能做什么

当我们的项目上线后可能遇到以下问题:

  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  5. 是否有一个全局视角来查看系统的运行状况?
  6. 有什么办法可以监控到JVM的实时运行状态?
  7. 怎么快速定位应用的热点,生成火焰图?
  8. 怎样直接从JVM内查找某个类的实例?

一般通过JDK自带的诊断工具去排查问题,不仅不好看而且需要记得很多的命令,而Arthas支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

官方文档:https://arthas.aliyun.com/doc/

准备

Arthas其实就是一个 jar 包,不需要安装,在GitHub可以下载,同时我也准备了Arthas 3.5.4版本(提取码:udhi)。

官方推荐使用arthas-boot,我们下载之后解压,如下:

image-20210906225109302

启动

通过以下命令启动:

java -jar arthas-boot.jar

image-20210906225836416

启动之后需要我们选择应用Java进程,我们这里选择 1 ,就成功启动了:

image-20210906230011440

常用命令

在官方文档的进阶使用介绍了 Arthas 的命令,我们也可以使用 help 查看帮助(全英文,我看不懂)。

image-20210906230509178

因为是全英文,如果英语不好的人只能望而却步了,但是看官方文档也是十分的方便。

dashboard

当前系统的实时数据面板。

注意在 arthas 中,有 tab 键填充功能,如果你只记得命令的开头几个字母,你可以 tab 补齐,所以比较好用。但是这个界面是实时刷新的,一般 5s 刷新一次,使用 q 键退出刷新(没有退出 arthas,如果要退出,使用ctrl+c )。

image-20210906232213880

thread

这个命令和 jstack 很相似,但是功能更加强大,主要是查看当前 JVM 的线程堆栈信息。

参数名称参数说明
id线程id
-n指定最忙的前 n 个线程并打印堆栈
-b找出当前阻塞其他线程的线程
-i value指定cpu使用率统计的采样间隔,单位为毫秒,默认值为200
-all显示所有匹配的线程

thread -i 1000 -n 3

每过 1000 毫秒进行采样,显示最占 CPU 时间的前 3 个线程。

image-20210907003700231

thread –b

找出阻塞当前线程的线程。

如下面一段代码:

/**
 * 类说明:演示死锁的产生,2个线程分别持有自己的锁,在不释放的情况下又想去获取对方的锁
 */
public class NormalDeadLock {

    private static Object lock1 = new Object();//第一个锁
    private static Object lock2 = new Object();//第二个锁

    //第一个拿锁的方法
    private static void lock1Do() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        synchronized (lock1) {
            System.out.println(threadName + " get lock1");
            Thread.sleep(100);
            synchronized (lock2) {
                System.out.println(threadName + " get lock2");
            }
        }
    }

    //第二个拿锁的方法
    private static void lock2Do() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        synchronized (lock2) {
            System.out.println(threadName + " get lock2");
            Thread.sleep(100);
            synchronized (lock1) {
                System.out.println(threadName + " get lock1");
            }
        }
    }

    //子线程代表lock2
    private static class Lock2 extends Thread {
        private String name;

        public Lock2(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            Thread.currentThread().setName(name);
            try {
                lock1Do();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //主线程代表lock1
        Thread.currentThread().setName("lock1");
        Lock2 lock2 = new Lock2("lock2");
        lock2.start();
        lock2Do();
    }
}

image-20210907002931404

如果有死锁,会有红色的字提醒着,这个阻塞的线程已经被另外一个线程,如下:

image-20210907003426319

thread --state WAITING

查看处于等待状态的线程。

image-20210907003919337

jvm

查看当前JVM信息。

image-20210907004027054

jad

反编译指定已加载类的,可以查看代码是否提交更新或者在排错的时候查看别人写的代码。

命令:jad 指定的类,可用tab补齐。

image-20210907004345705

trace

使用 trace 命令可以跟踪统计方法耗时。

比如使用一个 SpringBoot 项目,控制层 getOrder 方法调用了 orderService.getOrder(orderId),这个方法中分别进行 check、service、redis、mysql 等操作。就可以根据这个命令跟踪出来哪里的耗时最长。因此trace 能方便的帮助你定位和发现因 RT(Response Time,响应时间。一般系统RT 100ms 以内是比较正常的,300ms 勉强可以接受,1s 以上的话自己看着办吧。) 高而导致的性能问题缺陷,但其每次只能跟踪一级方法的调用链路。

栗子:

@RestController
@RequestMapping("/trace")
public class TraceController {

    @RequestMapping("/getOrder")
    public String getOrder(@RequestParam("orderId") String order) {
        StringBuffer buffer = new StringBuffer();
        //模拟check操作
        String check = check(order);
        String service = service(order);
        String redis = redis(order);
        String mysql = mysql(order);
        buffer.append(check).append(service).append(redis).append(mysql);
        return buffer.toString();
    }

    //检查订单编号是否正确
    public String check(String order) {
        try {
            Thread.sleep(50);
            return "订单编号check正常</br>";
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "fail";
    }

    //模拟业务操作
    public String service(String order) {
        try {
            Thread.sleep(60);
            return "通过订单编号业务操作service正常</br>";
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "fail";
    }

    //模拟redis查询
    public String redis(String order) {
        try {
            Thread.sleep(30);
            return "通过订单编号查询redis正常</br>";
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "fail";
    }

    //模拟mysql查询
    public String mysql(String order) {
        try {
            Thread.sleep(30);
            return "通过订单编号查询mysql正常</br>";
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "fail";
    }
}

访问:http://localhost:8080/trace/getOrder?orderId=2

image-20210907190329284

通过trace命令查看:

image-20210907190010835

monitor

方法执行监控。如每 5 秒统计一次某个类的某个方法的方法执行情况。

image-20210907191402403

watch

观察方法的入参出参信息。如:返回值、抛出异常、入参。

还是上面的代码,我们查看方法的入参和返回值可以这样:

watch cn.javatv.demo.controller.TraceController getOrder '{params[0],returnObj}'

image-20210907192136186

总结

Arthas 是一个很优秀的 java 诊断工具,无论是安装还是使用都很简洁,这里只是列出了部分,如果你需要学习使用,建议查看官方文档和手动尝试。

Arthas命令列表:

image-20210907195318674

参考:arthas使用介绍

  • 4
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汪了个王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值