使用Arthas高效排查线上问题:深入获取对象属性详情的技巧

这里是小奏 ,觉得文章不错可以关注公众号小奏技术

背景

线上运行中的项目有时候出了问题,总觉得比如是某个对象的属性值是有问题的,想查看一下。

或者是想看看线上某个线程池的队列是不是堆积了很多任务。

一般排查方式最简单最常用的就是,添加log比如

java 复制代码
log.info("xxx:{}",user.getName());

然后merge代码,发布,上线。

整套流程非常麻烦,查询完了可能还要再删除多余的log代码。

有些三方jar里面的对象甚至需要改源码,更麻烦。

有没有什么能动态查看JVM中的对象的属性值呢?

Arthas就可以做到

Arthas查看属性值

Arthas支持的功能很多,这里我们只专注如何查看线上运行JVM中对象中的属性值

vmtool命令

vmtool可以利用JVMTI接口,实现查询内存对象,强制 GC 等功能。

我们要查看一个对象的属性值,肯定得先获取这个对象。

java 复制代码
vmtool --action getInstances --className java.lang.String --limit 10

通过上面的命令就可以获取JVM中该对象的实例。

比如我要查看RocketMQ client中的MQClientInstance对象中的scheduledExecutorService属性值

我先直接给完整命令

java 复制代码
vmtool --action getInstances --className org.apache.rocketmq.client.impl.factory.MQClientInstance --limit 10 --express 'instances[0].getScheduledExecutorService()'

这里我解释下这个命令

  • --action getInstances 获取实例

  • --className org.apache.rocketmq.client.impl.factory.MQClientInstance 获取MQClientInstance对象

  • --limit 10 限制返回10个MQClientInstance对象,因为我们的JVM中运行的MQClientInstance对象有很多个,通过 --limit参数,可以限制返回值数量,避免获取超大数据时对 JVM 造成压力。默认值是 10个

  • --express 这里主要是代表后面拼接执行表达式,比如拿到了这10个对象之后要进行什么操作,后面拼接ognl语句

ognl相关的教程这里可以去官网学习更多ognl

  • 'instances[0].getScheduledExecutorService()' 这里就是ognl表达式,获取第一个MQClientInstance对象,然后调用getScheduledExecutorService方法

我们可以看看运行结果

java 复制代码
@DelegatedScheduledExecutorService[
    e=@ScheduledThreadPoolExecutor[java.util.concurrent.ScheduledThreadPoolExecutor@55c50cdd[Running, pool size = 1, active threads = 0, queued tasks = 35, completed tasks = 16556]],
    e=@ScheduledThreadPoolExecutor[java.util.concurrent.ScheduledThreadPoolExecutor@55c50cdd[Running, pool size = 1, active threads = 0, queued tasks = 35, completed tasks = 16556]],
]

如果我们加上-x 2参数,就可以看到更详细的信息, -x表示对象展开的层次,默认是1

java 复制代码
vmtool --action getInstances --className org.apache.rocketmq.client.impl.factory.MQClientInstance --limit 10 --express 'instances[0].getScheduledExecutorService()' -x 2
java 复制代码
@ScheduledFutureTask[
                sequenceNumber=@Long[298],
                time=@Long[5279358271057691],
                period=@Long[3600000000000],
                outerTask=@ScheduledFutureTask[java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@6230ff0f[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@60b5e6e0[Wrapped task = com.alibaba.ttl.TtlRunnable - org.apache.rocketmq.common.stats.StatsItemSet$5@278dbfb7]]],
                heapIndex=@Integer[19],
                this$0=@ScheduledThreadPoolExecutor[java.util.concurrent.ScheduledThreadPoolExecutor@102c2157[Running, pool size = 1, active threads = 0, queued tasks = 35, completed tasks = 90878]],
                state=@Integer[0],
                NEW=@Integer[0],
                COMPLETING=@Integer[1],
                NORMAL=@Integer[2],
                EXCEPTIONAL=@Integer[3],
                CANCELLED=@Integer[4],
                INTERRUPTING=@Integer[5],
                INTERRUPTED=@Integer[6],
                callable=@RunnableAdapter[java.util.concurrent.Executors$RunnableAdapter@60b5e6e0[Wrapped task = com.alibaba.ttl.TtlRunnable - org.apache.rocketmq.common.stats.StatsItemSet$5@278dbfb7]],
                outcome=null,
                runner=null,
                waiters=null,
                STATE=@FieldInstanceReadWrite[java.lang.invoke.VarHandleInts$FieldInstanceReadWrite@37880ac0],
                RUNNER=@FieldInstanceReadWrite[java.lang.invoke.VarHandleObjects$FieldInstanceReadWrite@3586e1e8],
                WAITERS=@FieldInstanceReadWrite[java.lang.invoke.VarHandleObjects$FieldInstanceReadWrite@640aede4],
            ],
            @ScheduledFutureTask[
                sequenceNumber=@Long[311],
                time=@Long[5304558271103660],
                period=@Long[86400000000000],
                outerTask=@ScheduledFutureTask[java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@32ddce18[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@37aad457[Wrapped task = com.alibaba.ttl.TtlRunnable - org.apache.rocketmq.common.stats.StatsItemSet$6@7867a6a4]]],
                heapIndex=@Integer[20],
                this$0=@ScheduledThreadPoolExecutor[java.util.concurrent.ScheduledThreadPoolExecutor@102c2157[Running, pool size = 1, active threads = 0, queued tasks = 35, completed tasks = 90878]],
                state=@Integer[0],
                NEW=@Integer[0],
                COMPLETING=@Integer[1],
                NORMAL=@Integer[2],
                EXCEPTIONAL=@Integer[3],
                CANCELLED=@Integer[4],
                INTERRUPTING=@Integer[5],
                INTERRUPTED=@Integer[6],
                callable=@RunnableAdapter[java.util.concurrent.Executors$RunnableAdapter@37aad457[Wrapped task = com.alibaba.ttl.TtlRunnable - org.apache.rocketmq.common.stats.StatsItemSet$6@7867a6a4]],
                outcome=null,
                runner=null,
                waiters=null,
                STATE=@FieldInstanceReadWrite[java.lang.invoke.VarHandleInts$FieldInstanceReadWrite@37880ac0],
                RUNNER=@FieldInstanceReadWrite[java.lang.invoke.VarHandleObjects$FieldInstanceReadWrite@3586e1e8],
                WAITERS=@FieldInstanceReadWrite[java.lang.invoke.VarHandleObjects$FieldInstanceReadWrite@640aede4],
            ]

可以看到很详细。

但是如果要继续获取到Queue里面的信息就比较麻烦了。因为

  1. ScheduledExecutorService仅仅是一个简单的接口,没有queue相关的属性方法
  2. ScheduledExecutorService接口的实现类DelegatedScheduledExecutorService也不是一个public类,是一个默认访问级别的内部类

所以要获取queue就很麻烦。得写一堆反射相关的ognl表达式 类似

java 复制代码
ognl -x 3 'targetClass=org.apache.rocketmq.client.impl.factory.MQClientInstance.class, targetClass.getDeclaredField("scheduledExecutorService").setAccessible(true), scheduledExecutorServiceField=targetClass.getDeclaredField("scheduledExecutorService"), scheduledExecutorServiceField.setAccessible(true), scheduledExecutorService=scheduledExecutorServiceField.get(targetObject), scheduledThreadPoolExecutor=@java.util.concurrent.ScheduledThreadPoolExecutor@class.cast(scheduledExecutorService), queue=scheduledThreadPoolExecutor.getQueue(), queue.toArray()'

比较麻烦,这里就没研究了。这个表达式不能直接使用,仅供参考

如果是获取BrokerFixedThreadPoolExecutor对象的任务就非常简单,使用

java 复制代码
vmtool --action getInstances --className org.apache.rocketmq.broker.latency.BrokerFixedThreadPoolExecutor --limit 100 --express "instances[5].workQueue.{#this.runnable}" -x 2

因为BrokerFixedThreadPoolExecutor直接继承了ThreadPoolExecutor

总结

可以看到Arthas对线上问题排查非常有帮助,效率也很高,还是要好好学习的。如果项目是基于docker部署的,推荐把arthas.jar打到基础镜像中。

相关推荐
拾光师几秒前
玩转springboot之springboot注册servlet
java
V+zmm101342 分钟前
springcloud分布式架构网上商城 LW +PPT+源码+讲解
java·数据库·后端·vue·idea·springclud
苏生Susheng27 分钟前
【Oracle】Oracle常用语句大全
java·数据库·sql·mysql·oracle·sql语句·数据库语法
拂衣28 分钟前
再谈TileMatrixSet,二维瓦片金字塔结构的标准定义(上)
java·gis
ZJ_.38 分钟前
Node.js 使用 gRPC:从定义到实现
java·开发语言·javascript·分布式·rpc·架构·node.js
基哥的奋斗历程39 分钟前
springboot整合Camunda实现业务
java·spring boot·dubbo
techlead_krischang1 小时前
中国软件评测中心最新报告:文心大模型技术、产品、应用全面领跑
后端·go
yjjpp23011 小时前
Django REST Framework(四)DRF APIVIEW
后端·python·django
fl_zxf1 小时前
JDK-SPI-服务提供者接口
java·jdk
concisedistinct1 小时前
大数据开发语言 Scala(四):面向对象编程
大数据·开发语言·后端·scala·编程语言·面向对象