arthas助力线上问题分析-基本使用篇

1 Arthas 介绍

1.1 Arthas是什么

Arthas 是Alibaba开源的Java诊断工具。它支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,
进一步方便进行问题的定位和诊断。

Arthas 官方文档十分详细,本文也参考了官方文档内容,同时在开源在的 Github 的项目里的 Issues 里不仅有问题反馈,更有大量的使用案例,也可以进行学习参考。

开源地址:https://github.com/alibaba/arthas

官方文档:https://alibaba.github.io/arthas

1.2 Arthas使用场景

当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?

  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?

  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?

  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!

  5. 是否有一个全局视角来查看系统的运行状况?

  6. 有什么办法可以监控到JVM的实时运行状态?

  7. 怎么快速定位应用的热点,生成火焰图?

1.3 Arthas怎么用

Arthas 是一款命令行交互模式的 Java 诊断工具,由于是 Java 编写,所以可以直接下载相应 的 jar 包运行。

1.3.1 standalone

  1. wget https://alibaba.github.io/arthas/arthas-boot.jar
  2. java -jar arthas-boot.jar执行后,选择需要检测的应用进程id即可

1.3.2 idea plugin

  1. 以idea为例,在应用市场搜索Alibaba Cloud View并点击安装
  2. 添加需要检测的host
  3. 点击more-dignostic,等待一会后,安装成功,选择需要检测的应用进程id即可

2 Arthas 基本使用篇

在了解了什么是 Arthas,以及 Arthas 的启动方式,下面详细介绍 Arthas 的常用使用方式。在使用命令的过程中如果有问题,每个命令都可以是 -h 查看帮助信息。

在这里插入图片描述

首先编写一个有各种情况的测试类运行起来,再使用 Arthas 进行问题定位。下面代码模拟了cpu过高,线程阻塞以及死锁。完整代码可以访问https://github.com/pj1987111/hongyinotes/tree/main/hongyiarthas获取。

@Slf4j
public class Problems {

    private static int CPU_THREADS = 10;
    private static ExecutorService executorService = Executors.newFixedThreadPool(CPU_THREADS);

    public static void start() {
        // 模拟 CPU 过高
        cpu();
        // 模拟线程阻塞
        thread();
        // 模拟线程死锁
        deadThread();
    }

    /**
     * 模拟cpu高损耗
     */
    private static void cpu() {
        for (int i = 0; i < CPU_THREADS; i++) {
            executorService.submit(new Thread(() -> {
                while (true) {
                    cpurun();
                }
            }));
        }
    }

    /**
     * 只有完整退出的方法才可以被热更新
     */
    private static void cpurun() {
        log.info("cpu start");
//        try {
//            Thread.sleep(10000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
    }

    /**
     * 模拟线程阻塞,向已经满了的线程池提交线程
     */
    private static void thread() {
        // 添加到线程
        executorService.submit(new Thread(() -> {
            while (true) {
                log.debug("thread start");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }));
    }

    /**
     * 死锁
     */
    private static void deadThread() {
        Object resourceA = new Object();
        Object resourceB = new Object();
        Thread threadA = new Thread(() -> {
            synchronized (resourceA) {
                log.info(Thread.currentThread() + " get ResourceA");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.info(Thread.currentThread() + "waiting get resourceB");
                synchronized (resourceB) {
                    log.info(Thread.currentThread() + " get resourceB");
                }
            }
        });

        Thread threadB = new Thread(() -> {
            synchronized (resourceB) {
                log.info(Thread.currentThread() + " get ResourceB");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.info(Thread.currentThread() + "waiting get resourceA");
                synchronized (resourceA) {
                    log.info(Thread.currentThread() + " get resourceA");
                }
            }
        });
        threadA.start();
        threadB.start();
    }
}

2.1 全局监控

首先可以执行dashboard命令,可以监控内存,GC,线程,运行环境等,如下图所示。
在这里插入图片描述

2.2 线程状态监控

2.2.1 CPU 状态监控

根据代码,可以看到cpu()方法是一个死循环打印,非常占用cpu。

输入thread 查看线程的CPU占用情况,将通过CPU占比倒序输出线程情况,可以看到前10个线程总计cpu占用100%。

[arthas@3599812]$ thread
Threads Total: 41, NEW: 0, RUNNABLE: 12, BLOCKED: 2, WAITING: 23, TIMED_WAITING: 4, TERMINATED: 0                                                                       
ID            NAME                                      GROUP                       PRIORITY      STATE         %CPU          TIME          INTERRUPTED   DAEMON        
33            pool-1-thread-2                           main                        5             RUNNABLE      10            0:18          false         false         
43            pool-1-thread-7                           main                        5             WAITING       10            0:18          false         false         
45            pool-1-thread-8                           main                        5             WAITING       10            0:18          false         false         
47            pool-1-thread-9                           main                        5             WAITING       10            0:18          false         false        ```

然后可以输入thread 线程id查看线程的堆栈信息,可以迅速定位到高cpu损耗的代码位置。

[arthas@3599812]$ thread 33
"pool-1-thread-2" Id=33 WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@77efb74e
    at sun.misc.Unsafe.park(Native Method)
    -  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@77efb74e
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
    at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
    at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
    at ch.qos.logback.core.OutputStreamAppender.writeBytes(OutputStreamAppender.java:197)
    at ch.qos.logback.core.OutputStreamAppender.subAppend(OutputStreamAppender.java:231)
    at ch.qos.logback.core.OutputStreamAppender.append(OutputStreamAppender.java:102)
    at ch.qos.logback.core.UnsynchronizedAppenderBase.doAppend(UnsynchronizedAppenderBase.java:84)
    at ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:51)
    at ch.qos.logback.classic.Logger.appendLoopOnAppenders(Logger.java:270)
    at ch.qos.logback.classic.Logger.callAppenders(Logger.java:257)
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值