Arthas

Arthas

目录

前言

Arthas是一款基于Java开发的开源诊断工具,用于解决生产环境中的Java应用程序故障。Arthas支持在应用程序运行时动态诊断和监控,并提供了一组强大的命令来分析和诊断Java应用程序

wiki:https://arthas.aliyun.com

安装

步骤1:下载Arthas

从Arthas官方网站上下载Arthas,下载链接如下:

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

步骤2:启动Arthas

运行以下命令以启动Arthas:

java -jar arthas-boot.jar

tip:如果Linux服务器上原先的JDK版本是OpenJDK,并且不支持 jps 命令,可以卸载OpenJDK并安装oracleJDK,并重启项目

rpm -qa|grep jdk
sudo rpm -e --nodeps java-1.8.0-openjdk-1.8.0.262.b10-1.el7.x86_64
sudo rpm -e --nodeps java-1.8.0-openjdk-headless-1.8.0.262.b10-1.el7.x86_64

步骤3:连接Java应用程序

在Arthas中,Arthas会列出服务器上所有java进程。按对应数字连接到Java应用程序:

Arthas的默认端口为3658,也可以手动指定端口号:

java -jar arthas-boot.jar --telnet-port 9998

还可以直接进入指定程序

java -jar arthas-boot.jar [pid]

一旦连接到Java应用程序,您可以使用Arthas提供的各种命令来分析和诊断应用程序。下面介绍一些常用命令。

常用命令

dashboard

作用:当前系统的实时数据面板

说明:

面板:

thread

作用:查看当前线程信息,查看线程的堆栈

参数:

trace

作用:方法内部调用路径,并输出方法路径上的每个节点上耗时

参数:

trace使用示例分析

https://arthas.aliyun.com/doc/trace.html

假设我们有一个简单的Spring Boot应用程序,其中有一个名为 UserService 的服务,它提供了以下三个方法:

@Service
public class UserService {
    public User getUserById(int id) {
        return userRepository.findById(id);
    }
    public User createUser(User user) {
        return userRepository.save(user);
    }
    public User updateUser(int id, User user) {
        User oldUser = userRepository.findById(id);
        if (oldUser == null) {
            throw new RuntimeException("User not found");
        }
        oldUser.setName(user.getName());
        return userRepository.save(oldUser);
    }
}

假设我们想要跟踪 UserService 接口中的所有方法,以找出哪些方法花费了最长的时间。我们可以使用以下命令:

trace com.example.UserService *

运行该命令后,Arthas将输出以下信息:

ts=2023-03-08 14:00:01;op=trace;num=1;cost=12;type=java.util.List;[User(id=1, name=John)]
ts=2023-03-08 14:00:02;op=trace;num=1;cost=5;type=void;
ts=2023-03-08 14:00:03;op=trace;num=1;cost=7;type=void;
ts=2023-03-08 14:00:04;op=trace;num=1;cost=10;type=void;
ts=2023-03-08 14:00:05;op=trace;num=1;cost=4;type=void;

上面的输出显示了每个方法的调用次数、总耗时和平均耗时等信息。从输出中可以看出,getUserById 方法是最耗时的方法,因为它的执行时间最长。

如果我们想查看 getUserById 方法中每个子方法的执行时间,可以使用以下命令:

trace com.example.UserService getUserById "**"

运行该命令后,Arthas将输出以下信息:

ts=2023-03-08 14:00:01;op=trace;num=1;cost=12;type=java.util.List;[User(id=1, name=John)]
ts=2023-03-08 14:00:01;[cost=11.583ms] [src=com.example.UserService.getUserById(UserService.java):15]
ts=2023-03-08 14:00:01;[cost=11.531ms] [src=com.example.UserRepository.findById(UserRepository.java):10]

上面的输出显示了 getUserById 方法中每个子方法的执行时间。从输出中可以看出,findById 方法是最耗时的子方法,因为它的执行时间最长。

如果我们想查看 findById 方法中每个子方法的执行时间,可以使用以下命令:

trace com.example.UserRepository findById "**"

运行该命令后,Arthas将输出以下信息:

ts=2023-03-08 14:00:01;[cost=11.531ms] [src=com.example.UserRepository.findById(UserRepository.java):10]
ts=2023-03-08 14:00:01;[cost=5.000ms] [src=org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java):651]
ts=2023-03-08 14:00:01;[cost=3.000ms] [src=org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java):697]

上面的输出显示了 findById 方法中每个子方法的执行时间。从输出中可以看出, queryForObject 方法是最耗时的子方法,因为它的执行时间最长。

动态trace:

https://arthas.aliyun.com/doc/trace.html#动态-trace

打开终端 1,trace 上面 demo 里的run函数,可以看到打印出 listenerId: 1

[arthas@59161]$ trace demo.MathGame run
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 112 ms, listenerId: 1
`---ts=2020-07-09 16:48:11;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
    `---[1.389634ms] demo.MathGame:run()
        `---[0.123934ms] demo.MathGame:primeFactors() #24 [throws Exception]

`---ts=2020-07-09 16:48:12;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
    `---[3.716391ms] demo.MathGame:run()
        +---[3.182813ms] demo.MathGame:primeFactors() #24
        `---[0.167786ms] demo.MathGame:print() #25

现在想要深入子函数primeFactors,可以打开一个新终端 2,使用telnet localhost 3658连接上 arthas,再 trace primeFactors时,指定listenerId

[arthas@59161]$ trace demo.MathGame primeFactors --listenerId 1
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 34 ms, listenerId: 1

这时终端 2 打印的结果,说明已经增强了一个函数:Affect(class count: 1 , method count: 1),但不再打印更多的结果。

再查看终端 1,可以发现 trace 的结果增加了一层,打印了primeFactors函数里的内容:

`---ts=2020-07-09 16:49:29;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
    `---[0.492551ms] demo.MathGame:run()
        `---[0.113929ms] demo.MathGame:primeFactors() #24 [throws Exception]
            `---[0.061462ms] demo.MathGame:primeFactors()
                `---[0.001018ms] throw:java.lang.IllegalArgumentException() #46

`---ts=2020-07-09 16:49:30;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
    `---[0.409446ms] demo.MathGame:run()
        +---[0.232606ms] demo.MathGame:primeFactors() #24
        |   `---[0.1294ms] demo.MathGame:primeFactors()
        `---[0.084025ms] demo.MathGame:print() #25

通过指定listenerId的方式动态 trace,可以不断深入。另外 watch/tt/monitor等命令也支持类似的功能。

或者使用命令:

trace -E com.test.ClassA|org.test.ClassB method1|method2|method3

trace 命令只会 trace 匹配到的函数里的子调用,并不会向下 trace 多层。因为 trace 是代价比较贵的,多层 trace 可能会导致最终要 trace 的类和函数非常多。

可以用正则表匹配路径上的多个类和函数,一定程度上达到多层 trace 的效果。

总结

Arthas是一个功能强大的Java应用程序诊断和故障排除工具

当然,知道了问题,还需要解决,这就是另一码事了

posted @ 2023-03-08 16:21  西红柿炒小番茄  阅读(511)  评论(0)    收藏  举报