Arthas 非常实用的几个命令

本文介绍几个非常有用的 Arthas 命令,熟练使用这几个命令,可以让我们在排查线上问题的时候更加轻松。

关于 Arthas 的设计思想,美团技术团队的文章Java 动态追踪技术探究一文中做了详细的说明,有兴趣可以去阅读一下。

trace

trace 命令见名知意,可以用于跟踪方法内部的调用路径,渲染整个调用链路上所有的性能开销和跟踪调用链路。

例如,我们提供了一个 controller,用来保存一笔订单数据,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.goku.order.rest;

import com.goku.order.entity.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;

/**
* @author fuyongde
* @date 2020/1/18 15:36
*/
@RestController
@RequestMapping("/api/orders")
@Slf4j
public class OrderRestController {

private static Random random = new Random();

@PostMapping
public Map<String, Object> save(@RequestBody Order order) {
long id = random.nextLong();
order.setId(id);
Date now = new Date();
if (Objects.nonNull(order.getFlag()) && order.getFlag()) {
log.info("调用路径1");
} else {
log.warn("调用路径2");
}
order.setCreateTime(now);
order.setUpdateTime(now);
log.info("request body : {}", order);
Map<String, Object> map = new HashMap<>(16);
map.put("order", order);
return map;
}
}

现在项目在运行中,我们要查看 save 方法的链路,打开 arthas 并选择我们的工程,使用 trace com.goku.order.rest.OrderRestController save 命令并触发一次调用

1
2
3
4
5
6
7
8
9
Affect(class-cnt:1 , method-cnt:1) cost in 69 ms.
`---ts=2020-01-18 22:40:56;thread_name=http-nio-8081-exec-1;id=14;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@4f824872
`---[3.6313ms] com.goku.order.rest.OrderRestController:save()
+---[0.0164ms] com.goku.order.entity.Order:setId() #26
+---[0.0047ms] com.goku.order.entity.Order:getFlag() #28
+---[0.1695ms] org.slf4j.Logger:warn() #31
+---[0.0103ms] com.goku.order.entity.Order:setCreateTime() #33
+---[0.0062ms] com.goku.order.entity.Order:setUpdateTime() #34
`---[2.6388ms] org.slf4j.Logger:info() #35

从输出上来看,我们可以很清楚看到代码的 26、28、31、33、34、35 行被执行了,同样的,如果 order 对象的 flag 属性如果为 true,则第 31 行的调用会变成第 29 行,如下:

1
2
3
4
5
6
7
8
`---ts=2020-01-18 22:56:00;thread_name=http-nio-8081-exec-4;id=17;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@4f824872
`---[2.1072ms] com.goku.order.rest.OrderRestController:save()
+---[0.0051ms] com.goku.order.entity.Order:setId() #26
+---[min=0.0023ms,max=0.0023ms,total=0.0046ms,count=2] com.goku.order.entity.Order:getFlag() #28
+---[0.6911ms] org.slf4j.Logger:info() #29
+---[0.0186ms] com.goku.order.entity.Order:setCreateTime() #33
+---[0.0032ms] com.goku.order.entity.Order:setUpdateTime() #34
`---[0.312ms] org.slf4j.Logger:info() #35

从输出结果也可以清楚的看到代码在执行过程中,哪一行的代码调用耗时最长,这对我们排查线上对应的问题是非常有帮助的。

包含 jdk 的函数

如果需要将 JDK 的方法执行链路也输出,则使用 --skipJDKMethod false 参数,例如: trace com.goku.order.rest.OrderRestController save --skipJDKMethod false

根据调用耗时过滤

如果需要只需要查看调用耗时比较长的,则添加对应的过滤条件,可以更快的帮助我们定位异常问题。比如只查询调用耗时超过 10ms 的方法:trace com.goku.order.rest.OrderRestController save '#cost > 10'