Arthas

前言

这里大部分介绍一下watch、tt这些实时观察方法执行的命令,这些命令的原理都通过字节码增强技术来实现的(动态插入字节码),会在指定类的方法中插入一些切面来实现数据统计和观测,可以通过jad命令查看增强后的字节码,如果观察的对象是接口,那么会把接口所有子类的字节码都增强,去哪儿网的开源项目Bistoury有个在线debug的功能,也是用到了字节码增强技术

另外,下面的命令都涉及到表达式,而且表达式的作用相当重要,表达式具体查看官方参考文档

ognl

ognl (Object-Graph Navigation Language) 是一种强大的表达式语言,用于获取和设置 Java 对象的属性,允许开发者通过简洁的语法访问复杂的对象图结构,从而简化了数据绑定、类型转换等任务,包括mybatis在内的很多框架都用到了ognl表达式

arthas的所有表达式就采用ognl,包括对象访问表达式、方法调用表达式、条件判断表达式等,所以了解ognl是很有必要的,arthas ognl表达式的特殊用法

arthas的ognl表达式的root是一个Advice类的实例,所以表达式中可以直接访问params、target、returnObj等变量,获取非静态变量的表达式都必须基于Advice实例,表达式核心变量

实际用的时候发现了一个问题,4.0.3版本不支持将returnObj这些对象作为ognl调用静态方法的参数,而4.0.4没有这个问题,经过测试,4.0.3版本的Ognl有问题,可以分别引入两个版本的arthas-core.jar包测试以下代码

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
import cn.hutool.core.map.MapUtil;
import com.taobao.arthas.core.command.express.DefaultMemberAccess;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;

import java.util.Map;

public class Person {

private String name;

private String code;

public Person(String name, String code) {
this.name = name;
this.code = code;
}

public static void main(String[] args) throws OgnlException {
OgnlContext ognlContext = new OgnlContext(new DefaultMemberAccess(true), null, null, null);
Map<Object, Object> map = MapUtil.builder().put("person", new Person("张三", "zs0001")).build();
Object value = Ognl.getValue("@cn.hutool.json.JSONUtil@toJsonStr(person)", ognlContext, map);
System.out.println(value);
}

public String getName() {
return name;
}

public String getCode() {
return code;
}
}

watch

watch命令主要的作用就是查看方法执行过程中的数据,个人认为相当于低配版的debug,可以查看入参、执行类实例、返参,并且可以通过ognl语句查看更详细的数据

这里使用Web UI举个例子,下面是测试的方法,我们通过watch命令调用fastjson的toJSONString方法,获取返回参数的json字符串

1
2
@Select("select ${selectFields} from ${tbne} t ${ew.customSqlSegment}")
Page<Map> page(IPage<Map> page, @Param("tbne") String tbne, @Param("selectFields") String selectFields, @Param(Constants.WRAPPER) Wrapper wrapper);

可以看到成功返回了经过toJSONString转换的page方法返回参数的json字符串,这里实际执行的命令是

1
watch  -f com.example.AssetOneMapMapper page -x 1  '@com.alibaba.fastjson.JSON@toJSONString(returnObj)'
  1. -f:代表在函数结束之后(正常返回和异常返回)观察,watch支持在执行前、异常返回时、正常返回时观察,具体参考文档
  2. com.example.Mapper page:要观察的类以及方法,arthas支持通过表达式匹配,即可以匹配多个类或者方法
  3. -x:表示遍历深度,比如入参有一个List,为1的时候只会展示List本身(即List的toString),那么为2的时候将会展示每个元素的内容,依次类推,默认值是 1,最大为4
  4. ‘@com.alibaba.fastjson.JSON@toJSONString(returnObj)’:ognl表达式,表示使用JSON类的静态方法toJSONString转换returnObj,returnObj为观察方法的返回参数

tt

tt(TimeTunnel )命令的主要就是记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测,可以立即为方法执行数据的时空隧道,是watch命令的高配版,主要缺点是内存问题,需要手动释放,不然可能导致OOM,有两个比较重要的功能

  1. 查看数据:tt命令会记录每次执行涉及到的数据,然后我们可以通过命令查看记录的数据(对象的内部变量也可以查看),这一点比watch更强大,如果需要通过watch查看某个对象数据的内部变量,需要提交watch命令,然后再触发方法,每次更改需要重复这些操作,比较麻烦,而tt命令在这方面更简单
  2. 重新调用:tt命令可以通过id重新调用记录的具体某一次方法的执行

这里使用Web UI举个例子,测试的方法仍然使用上面watch测试的方法,我们调用tt命令记录方法执行

1
2
@Select("select ${selectFields} from ${tbne} t ${ew.customSqlSegment}")
Page<Map> page(IPage<Map> page, @Param("tbne") String tbne, @Param("selectFields") String selectFields, @Param(Constants.WRAPPER) Wrapper wrapper);

这里可以看到记录下了两次调用,这里参数没展示全,实际上还有returnObj以及throwExp,可以自行测试,这里实际执行的命令是

1
tt -t com.example.Mapper page
  1. -t:表示希望记录下方法的每次执行情况,在记录时使用该参数,和-p、-w都属于主参数
  2. com.example.Mapper page:表示要观察的类以及方法,arthas支持通过表达式匹配,即可以匹配多个类或者方法

查看数据

上面已经记录下了方法的执行情况,我们可以通过tt -w命令查看某一次记录的数据,需要使用ognl表达式,比如我现在想要查看page方法第四个入参wrapper的customSqlSegment的值,使用以下命令

1
tt -w 'params[3].customSqlSegment' -x 1 -i 1005
  1. -w ‘params[3].customSqlSegment’:-w表示希望查看某一次记录的数据,后面是ognl表达式,即要查看的具体值(在上面的watch命令中有介绍),这里不仅可以查看数据,还可以调用方法,和-t、-p都属于主参数
  2. -x 1:表示便利深度为1,既不展示数据内部的值
  3. -i 1005:表示要查看的记录编号

可以看到返回了customSqlSegment具体的值

重新调用

想要使用重新调用的功能,直接点击Web UI上的invoke按钮即会重新调用,具体命令为

1
tt -i 1005 -p
  1. -i 1005:代表要重新调用的记录
  2. -p:表示希望重新调用某一次记录,和-t、-w都属于主参数

trace

trace命令的主要作用就是查看方法内部调用路径以及路径上的每个节点上耗时,不过trace只支持跟踪一级方法的调用链路,下个要继续查看下一级方法,可以使用动态trace,另外trace默认不会返回jdk方法的耗时,需要显式设置--skipJDKMethod false

这里使用Web UI举个例子,下面是测试的方法,我们通过trace命令查看这个方法的耗时

1
2
3
4
5
6
7
8
9
10
11
private void response(ChannelHandlerContext ctx, Http2FrameStream requestStream, String responseBody) {
DefaultHttp2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(
new DefaultHttp2Headers().status("200")
.set(HttpHeaderNames.DATE, DateTime.now().toString())
.set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8")
.set(HttpHeaderNames.CONTENT_LENGTH, String.valueOf(responseBody.length()))
).stream(requestStream);
DefaultHttp2DataFrame dataFrame = new DefaultHttp2DataFrame(Unpooled.wrappedBuffer(responseBody.getBytes()), true).stream(requestStream);
ctx.writeAndFlush(headersFrame);
ctx.writeAndFlush(dataFrame);
}

可以看到返回response方法下各个方法的耗时,这里实际执行的命令是

1
trace com.example.netty.server.Https2Server$Http2ServerHandler response

这里仅涉及到需要观察的类以及方法,arthas支持通过表达式匹配,即可以匹配多个类或者方法,其他命令参考官网

stack

stack命令的主要作用就是观察方法被调用的调用路径,在实际中可以用来确定当前方法是怎么被调用的,个人认为也可以理解为低配版的debug

这里使用Web UI举个例子,下面是测试的方法,我们通过stack命令查看这个方法的被调用路径

1
2
3
4
5
6
7
8
9
10
11
private void response(ChannelHandlerContext ctx, Http2FrameStream requestStream, String responseBody) {
DefaultHttp2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(
new DefaultHttp2Headers().status("200")
.set(HttpHeaderNames.DATE, DateTime.now().toString())
.set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8")
.set(HttpHeaderNames.CONTENT_LENGTH, String.valueOf(responseBody.length()))
).stream(requestStream);
DefaultHttp2DataFrame dataFrame = new DefaultHttp2DataFrame(Unpooled.wrappedBuffer(responseBody.getBytes()), true).stream(requestStream);
ctx.writeAndFlush(headersFrame);
ctx.writeAndFlush(dataFrame);
}

可以看到,返回了调用链路、classloader、线程id以及线程名,这里实际的命令是

1
stack com.example.netty.server.Https2Server$Http2ServerHandler response

这里只涉及到了观察的类以及方法,arthas支持通过表达式匹配,即可以匹配多个类或者方法,其他参数参考官网

monitor

monitor命令的主要作用就是对观察方法的调用进行监控(对一定时间内方法执行情况进行统计),这里不详细介绍了

retransform

这个命令是单独的,和上面的命令不是一个原理,动态插入字节码可以理解为修改class的字节码,而retransform是替换class的字节码

retransform的作用是重新加载class文件,可以用来实现热部署,一般可以配合jad(反编译)、mc(编译)两个命令来使用

在这里介绍是因为平时用的可能也比较多,而且有个小问题记录一下,通过retransform替换class字节码后,通过stop关闭arhtas,重新打开arthas后,使用jad命令,会发现被替换过的类还原成替换之前的了,在实际使用时需要注意,参考:https://github.com/alibaba/arthas/issues/1891#issuecomment-900784364


本站总访问量:次本站总访问数:人
本站由 Peng Tong 使用 Stellar 1.29.1 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。