[toc]
简介
Arthas中文名叫做阿尔萨斯
官方地址:alibaba.github.io/arthas/
安装
使用arthas-boot
(推荐)
下载arthas-boot.jar,然后用java -jar的方式启动:
curl -O https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar
打印帮助信息
java -jar arthas-boot.jar -h
如果下载速度比较慢,可以使用aliyun的镜像:
java -jar arthas-boot.jar --repo-mirror aliyun --use-http
如果从github下载有问题,可以使用gitee镜像
curl -O https://arthas.gitee.io/arthas-boot.jar
通过as.sh命令启动
Mac安装as.sh,并设置快捷方式
curl -sk https://arthas.gitee.io/arthas-boot.jar -o ~/.arthas-boot.jar && echo "alias as.sh='java -jar ~/.arthas-boot.jar --repo-mirror aliyun --use-http'" >> ~/.bashrc && source ~/.bashrc
如果安装了zsh执行下面命令
curl -sk https://arthas.gitee.io/arthas-boot.jar -o ~/.arthas-boot.jar && echo "alias as.sh='java -jar ~/.arthas-boot.jar --repo-mirror aliyun --use-http'" >> ~/.zshrc && source ~/.zshrc
接下来只需要命令行输入as.sh就可以启动
通过Cloud Toolkit插件直接进入arthas
卸载
-
在 Linux/Unix/Mac 平台
删除下面文件:
rm -rf ~/.arthas/ rm -rf ~/logs/arthas
-
Windows平台直接删除user home下面的
.arthas
和logs/arthas
目录
进入arthas
-
先启动需要被诊断的程序,再启动arthas
java -jar arthas-boot.jar
-
选择相应的进程id
-
看到如下页面表示arthas,attach成功
-
接下来就可以使用arthad相关命令进行诊断程序
退出arthas
如果只是退出当前的连接,可以用quit
或者exit
命令。Attach到目标进程上的arthas还会继续运行,端口会保持开放,下次连接时可以直接连接上。
如果想完全退出arthas,可以执行stop
命令。
快速入门
常用命令
dashboard:仪表板
作用:显示当前系统的实时数据面板,按q或ctrl+c退出
jad:反编译某个类,或者反编译某个类的某个方法
作用:把字节码文件反编译成源码
反编译类:
jad com.shinemo.todo.domain.TodoDO
反编译method:
显示编译后的详情
jad com.shinemo.wangge.core.service.todo.impl.TodoServiceImpl operateTodoThing
只显示源码
jad --source-only com.shinemo.wangge.core.service.todo.impl.TodoServiceImpl operateTodoThing
thread 线程相关命令
作用:查看当前JVM的线程堆栈信息
thread -n:排列出 CPU 使用率 Top N 的线程。
thread id: 显示指定线程的运行堆栈
thread -b: 找出当前阻塞其他线程的线程
死锁案例:
@GetMapping("/test")
@SmIgnore
public ApiResult<String> test() {
/** 创建资源 */
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();
return ApiResult.success("success");
}
sc:查看JVM已加载的类信息
sc默认开启了子类匹配功能,所以可以用来查看哪些类实现了这个接口.
sc -d:显示详细信息
sc -f:显示所有成员变量,需要配合-d一起使用
sc com.shinemo.wangge.core.handler.UrlRedirectHandler
sm:查看已加载类的方法信息
sm -d:显示详细信息
sm com.shinemo.wangge.web.controller.todo.TodoController *
sm com.shinemo.wangge.web.controller.todo.TodoController getTypeList
watch 方法执行的数据观测
当我们遇到线上数据 bug
时,我们一般处理的手段就是开发环境模拟线上数据,从生产日志中查找线索,再或者远程 debug
。以上不管哪种排查手段,相对都是比较麻烦。这时Arthas的 watch
可以帮助我们查看实时的代码执行情况。使用观察表达式可以查看函数的 参数
, 返回值
, 异常信息
。观察表达式主要由 OGNL
表达式组成,所以可以编写 OGNL
表达式来执行。
语法:
watch 包名.类名 方法名 想要查看的信息
例子:
watch com.shinemo.wangge.web.controller.todo.TodoController getTodoList '{params,returnObj,throwExp}' -n 5 -x 5 '1==1'
-x表示遍历深度,可以调整来打印具体的参数和结果内容,默认值是1。
-n表示执行次数
查看函数返回值
watch com.shinemo.wangge.web.controller.common.IndexController getIndex returnObj
查看函数的请求参数
watch com.shinemo.wangge.web.controller.common.IndexController getIndex params
异步保存日志
有时我们排查某个函数,不能马上获取到函数的信息, arthas
给提供的 后台异步任务
可以帮助我们记录日志。使用方式和Linux的类似。
watch com.shinemo.wangge.web.controller.todo.TodoController getTodoList '{params,returnObj,throwExp}' -n 5 -x 5 '1==1' > /data/logs/test.log &
trace:输出方法调用路径,并输出耗时
我们常会遇到调用某个api时rt过长,我们就要找出调用链上的某个或几个函数进行优化,我们通常定位几个可能的锚点,打印各个锚点间的rt。或者从日志中找出日志打印的时间点计算出时间差,不管使用哪种方法都比较繁琐。当使用 arthas
的 trace
命令可以轻松的完成我们的需求。
这个指令对于优化代码非常的有用,可以看出具体每个方法执行的时间,如果是for循环等重复语句,还能看出n次循环中的最大耗时,最小耗时,和平均耗时.
trace com.shinemo.wangge.web.controller.common.IndexController getIndex -n 5 '1==1'
结果如下:
[arthas@13090]$ trace com.shinemo.wangge.web.controller.common.IndexController getIndex -n 5 '1==1'
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 233 ms, listenerId: 3
`---ts=2020-07-08 14:13:09;thread_name=http-nio-20014-exec-4;id=64;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@11e1bd43
`---[652.290636ms] com.shinemo.wangge.web.controller.common.IndexController:getIndex()
+---[0.030585ms] com.shinemo.smartgrid.domain.SmartGridContext:getLongUid() #80
+---[0.012105ms] com.shinemo.smartgrid.domain.SmartGridContext:getMobile() #81
+---[88.923084ms] com.shinemo.wangge.core.service.stallup.StallUpService:getSimpleInfo() #82
+---[0.012727ms] com.shinemo.common.tools.result.ApiResult:isSuccess() #83
+---[0.009893ms] com.shinemo.common.tools.result.ApiResult:getData() #87
tt:官方名为时空隧道
watch
可以排查函数的调用情况,比较适用在已知当次调用可能存在的情况后,查看信息。如果一个函数调用n次后,有几次为执行异常,我们要去找出这些异常的调用,在 watch
中排查就不怎么方便了。使用 tt
命令可以较方便查看异常的调用及信息
你对某方法开启tt后,会记录下每一次的调用(你需要设置最大监控次数),然后你可以在任何时候会看这里面的调用,包括出参,入参,运行耗时,是否异常等
tt -t com.shinemo.wangge.web.controller.todo.TodoController getTodoList -n 5
返回:
查看方法查看调用信息
tt -w '{method.name,params,returnObj,throwExp}' -x 3 -i 1000
重新触发一次
tt -p -i 1000
重新触发5次,每次间隔2秒
tt -p --replay-times 5 --replay-interval 2000 -i 1000
获取所有调用记录
tt -l
删除所有调用记录
tt --delete-all
stack:观察方法的调用路径
使用 stack命令查看方法的调用信息。
monitor:统计方法耗时
使用 monitor 命令监控统计方法的执行情况,比如指定时间内请求的总次数,成功次数,失败次数,平均响应时长,失败比例。
-c:表示统计周期,默认值为60秒
monitor com.shinemo.wangge.web.controller.todo.TodoController getTodoList -c 10
redefine:热更新
常用步骤
- 通过jad命令反编译,然后用vim来修改源码
- 通过mc命令来将修改后的代码编译为class文件
- 用redefine命令加载新的字节码文件
jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java
mc /tmp/UserController.java -d /tmp
redefine /tmp/com/example/demo/arthas/user/UserController.class
注意事项
- redefine的class不能修改、添加、删除类的field和method,包括方法参数、方法名称及返回值。
新增和修改,删除field,会抛异常:redefine error! java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)
新增方法会抛异常:redefine error! java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
修改或删除方法会抛异常:redefine error! java.lang.UnsupportedOperationException: class redefinition failed: attempted to delete a method
- 正在跑的函数,没有退出不能生效,比如下面新增加的
System.out.println
,只有run()
函数里的会生效
public class MathGame {
public static void main(String[] args) throws InterruptedException {
MathGame game = new MathGame();
while (true) {
game.run();
TimeUnit.SECONDS.sleep(1);
// 这个不生效,因为代码一直跑在 while里
System.out.println("in loop");
}
}
public void run() throws InterruptedException {
// 这个生效,因为run()函数每次都可以完整结束
System.out.println("call run()");
try {
int number = random.nextInt();
List<Integer> primeFactors = primeFactors(number);
print(number, primeFactors);
} catch (Exception e) {
System.out.println(String.format("illegalArgumentCount:%3d, ", illegalArgumentCount) + e.getMessage());
}
}
异步保存请求到文件
1.使用&在后台执行任务
watch com.shinemo.wangge.web.controller.common.IndexController getIndex '{params,returnObj,throwExp}' -n 5 -x 3 '1==1' &
这时命令会在后台执行,可以在console中继续执行其他命令。
使用jobs查看所有后台任务
使用kill杀死任务
2.使用>>将任务输出重定向
可通过>
或者>>
将任务输出结果输出到指定的文件中,可以和&
一起使用,实现arthas命令的后台异步任务。比如:
#指定文件
watch com.shinemo.wangge.web.controller.common.IndexController getIndex '{params,returnObj,throwExp}' -n 5 -x 3 '1==1' >> test.out &
#不指定文件
watch com.shinemo.wangge.web.controller.common.IndexController getIndex '{params,returnObj,throwExp}' -n 5 -x 3 '1==1' >> &
默认会保存到~/logs/arthas-cache/${PID}/${JobId}
3.命令执行结果存日志
默认情况下,该功能是关闭的,如果需要开启,请执行以下命令:
options save-result true
命令的执行结果会异步保存在:{user.home}/logs/arthas-cache/result.log
,请定期进行清理,以免占据磁盘空间。
4.退出arthas继续执行后台任务
如果不想停止arthas,继续执行后台任务,可以执行 quit
退出arthas控制台(stop
会停止arthas 服务)
Arthas支持Web Console
成功启动连接进城之后就已经自动启动,可以直接访问:http://localhost:8563/ 页面上的操作模式和控制台完全一样.
Arthas 支持 管道命令
Arthas支持使用管道对上述命令的结果进行进一步的处理,如sm org.apache.log4j.Logger | grep
- grep——搜索满足条件的结果
- plaintext——将命令的结果去除颜色
- wc——按行统计输出结果