arthas入门最佳实践(代码片段)

汪小哥 汪小哥     2022-12-10     272

关键词:

一、入门步骤

1、安装

https://arthas.gitee.io/install-detail.html
上述命令会下载启动脚本文件 as.sh 到当前目录

curl -L https://alibaba.github.io/arthas/install.sh | sh

or
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

2、在线教程体验

https://alibaba.github.io/arthas/arthas-tutorials?language=cn
当然也可以自己本地体验一下~自己通过下载一个 arthas-idea-plugin的体验demo 直接本地上手
https://github.com/WangJi92/arthas-plugin-demo

全局命令说明

  • -x 是展示结果属性遍历深度,默认为 1
  • -n 是执行的次数 ,q 退出
  • -c classloader 的hash值
  • 退出 q ,关闭 stop

3、了解最常用的trace、watch的功能

watch和trace 是arthas 诊断中对于开发人员解决线上的问题最常用的功能!

trace

基本示例

trace com.wangji92.arthas.plugin.demo.controller.CommonController getRandomInteger -n 5 '1==1'

https://arthas.gitee.io/trace.html

  • 性能优化~
  • 调用的这个方法,走的具体流程是咋样的!可以通过调用链看出来。
  • 有异常了可以查看异常的堆栈

高级的功能

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


trace -E xxxClassA|xxxClassB method1 | method2

trace -E com.wangji92.arthas.plugin.demo.controller.CommonController|com.wangji92.arthas.plugin.demo.service.ArthasTestService traceE|doTraceE -n 5 '1==1'

watch

https://arthas.gitee.io/watch.html
wathc 从字面上理解就是观察值的信息,可以查看入参、返回值、异常、可以执行表达式获取静态变量、target.xxx调用目标实施的字段、方法等等都行只要你想得到没有做不到的

基本示例

watch com.wangji92.arthas.plugin.demo.controller.CommonController traceE 'params,returnObj,throwExp' -n 5 -x 3 '1==1'

4、arthas 表达式核心变量

public class Advice 
 
    private final ClassLoader loader;
    private final Class<?> clazz;
    private final ArthasMethod method;
    private final Object target;
    private final Object[] params;
    private final Object returnObj;
    private final Throwable throwExp;
    private final boolean isBefore;
    private final boolean isThrow;
    private final boolean isReturn;
    
    // getter/setter  
  

从watch 和 trace 中看到 后面的 ‘1==1’ 执行的是一个条件表达式 当值为true 的时候通过执行了一个ognl 表达式 ,watch 观察 params,returnObj,throwExp 入参、返回值、是否异常 这个也是一个表达式,那么这个到底是咋回事?

spring el 表达式

没有学习过ognl 使用多年的spring 一定知道他的el 表达式,el 表达式中也有一种概念叫做【Context 上下文,和表达式】 如下所示,因为有了simple这个上下文 才能解析 “booleanList[0]” 这个脚本的含义~ 这个很熟悉,很好理解,那么ognl 表达式一样不难了。

class Simple 
    public List<Boolean> booleanList = new ArrayList<Boolean>();


Simple simple = new Simple();

simple.booleanList.add(true);

StandardEvaluationContext simpleContext = new StandardEvaluationContext(simple);

// false is passed in here as a string. SpEL and the conversion service will
// correctly recognize that it needs to be a Boolean and convert it
parser.parseExpression("booleanList[0]").setValue(simpleContext, "false");

// b will be false
Boolean b = simple.booleanList.get(0);

ognl 表达式

arthas 也是一样的,只是使用了一个叫做ognl的脚本,核心变量就是他的上下文,可以直接获取到这些字段。watch 观察的这几个字段 params,returnObj,throwExp 也就是我们所谓的上下文的概念,观察参数、返回值、和异常的信息。
如下是arthas 源码中 表达式评估和watch 观察值执行的代码!Advice 就是一个上下文,这里还增加了一个变量 const。知道了这些那不是很简单??
com.taobao.arthas.core.advisor.ReflectAdviceListenerAdapter#isConditionMet

/**
     * 判断条件是否满足,满足的情况下需要输出结果
     * @param conditionExpress 条件表达式
     * @param advice 当前的advice对象
     * @param cost 本次执行的耗时
     * @return true 如果条件表达式满足
     */
    protected boolean isConditionMet(String conditionExpress, Advice advice, double cost) throws ExpressException 
        return StringUtils.isEmpty(conditionExpress) ||
                ExpressFactory.threadLocalExpress(advice).bind(Constants.COST_VARIABLE, cost).is(conditionExpress);
    

    protected Object getExpressionResult(String express, Advice advice, double cost) throws ExpressException 
        return ExpressFactory.threadLocalExpress(advice)
                .bind(Constants.COST_VARIABLE, cost).get(express);
    

表达式实践

arthas 群经常有人问重载方法如何判断,无非就是评估条件? 参数的个数、第一个参数是什么?返回值的类型等等都可以作为你评估的条件。如下的watch 前面的一段是观察的值、后面这一段是表达式评估 ,满足了条件才执行。


入参长度大于0

 watch com.wangji92.arthas.plugin.demo.controller.CommonController traceE 'params,returnObj,throwExp' -n 5 -x 3 'params.length >0'

返回值为String 且长度大于5

watch com.wangji92.arthas.plugin.demo.controller.CommonController traceE 'params,returnObj,throwExp' -n 5 -x 3 'returnObj instanceof java.lang.String && returnObj.length>5'

条件表达式+异步任务

  • 只有特定的场景才会有bug ,如何排查bug?
  • 一天只出现一两次如何解决?

条件表达式主要是用来过滤使用,比如某些场景只是在特定的参数才会出现,肯能会花费很多的时间去等待,这个时候可以使用条件表达式过滤 +异步任务 更多参考博客

5、ognl 表达式

https://arthas.gitee.io/ognl.html 从上面看,ognl 在watch、trace上面无所不能啊,其实还有tt 也是 使用ognl 表达式执行逻辑的. @xxxClas@xxxStaticField 是静态变量的语法糖 ognl的,好好看一下官方的文档。OGNL特殊用法请参考:https://github.com/alibaba/arthas/issues/71

获取静态变量

静态变量由于 一个jvm 中可能被多个classloader加载,jvm 认定为一个实例是一个classloader加载哦,所以需要知道当前静态类的hash 值(sc -d com.wangji92.arthas.plugin.demo.controller.StaticTest)可以通过这个命令获取。

ognl  -x  3 '@com.wangji92.arthas.plugin.demo.controller.StaticTest@INVOKE_STATIC_DOUBLE' -c e374b99

调用spring 方法?

watch 执行ognl 语法中获取spring context 然后进行调用bean的方法

watch -x 3 -n 1  org.springframework.web.servlet.DispatcherServlet doDispatch '@org.springframework.web.context.support.WebApplicationContextUtils@getWebApplicationContext(params[0].getServletContext()).getBean("commonController").getRandomInteger()'

ognl 执行静态的一个spring context 然后调用bean 的方法

ognl -x 3 '#springContext=@com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider@context,#springContext.getBean("commonController").getRandomInteger()' -c e374b99

有没有起飞的感觉,无所不能!前提是你要掌握一些ognl的一些简单的语法!

6、完毕

对于线上排查问题,我感觉这几个命令够你用了,还有一些其他的反编译、火焰图、… 时间隧道、logger 等级修改,jvm环境信息等等感觉是有频率都没有上面的高,毕竟jvm信息有专门的监控~即使没有arthas 你也可以找到更好的工具去分析堆栈,jvm故障。


完了?
啊?这么多命令 记不住啊 还有一些高级的ognl的语法凉了… 让你获取一下所有的spring的环境变量咋办?trace、watch 这两个命令我还没有体验够呢?更加高级的让我如何是好啊!好了,请看下文。

二、进阶

前提

前提是你对于arthas 有了大概的理解,基本上的命令都有点概念了,ognl 简单的语法能够看懂了… 简单的条件表达式会用了。 之前我们所过arthas的命令这么多 要记住小本本少不了啊!难受想哭~ 不要急,汪小哥来给你解决问题,
目前Arthas 官方的工具还不够足够的简单,需要记住一些命令,特别是一些扩展性特别强的高级语法,比如ognl获取spring context 为所欲为,watch、trace 不够简单,需要构造一些命令工具的信息,因此只需要一个能够简单处理字符串信息的插件即可使用。当在处理线上问题的时候需要最快速、最便捷的命令,因此arthas idea 插件还是有存在的意义和价值的。

arthas idea plugin

这个插件的意义不是处理协议层面的问题,主要解决命令生成的问题,由于工程在idea 里面管理,你想想你要watch 哪个类,这个插件是知道的,帮助你更方便、更加快捷的构建命令。使用arthas idea 插件 这一点一定要理解哦!主要解决你如何构造命令的问题! 更多查看文档

解决的问题

  • spring 环境变量优先级问题
  • 获取静态变量
  • 火焰图集成
  • logger 命令集成
  • 反编译集成
  • trace -E 集成
  • tt 集成

… 基本上你能够在arths 上面看到的功能都集成到了这个上面!直接在idea 里面搜索arths idea 即可安装。

常用特殊用法问题

静态变量

可以直接获取 ognl 获取

ognl  -x  3 '@com.wangji92.arthas.plugin.demo.controller.StaticTest@INVOKE_STATIC_DOUBLE' -c e374b99

可以通过watch 获取 (光标放置在字段上)

watch com.wangji92.arthas.plugin.demo.controller.StaticTest * 'params,returnObj,throwExp,@com.wangji92.arthas.plugin.demo.controller.StaticTest@INVOKE_STATIC_DOUBLE' -n 5 -x 3 '1==1'

一般的变量

可以通过spring context.getBean().field 获取(这个是要配置一个静态的spring context 看使用文档)
tt 、watch 也是可以的哦~ 一样的原理

ognl -x 3 '#springContext=@com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider@context,#springContext.getBean("staticTest").filedValue' -c e374b99

watch 获取 放置在字段上即可

watch com.wangji92.arthas.plugin.demo.controller.StaticTest * 'params,returnObj,throwExp,target.filedValue' -n 5 -x 3 'method.initMethod(),method.constructor!=null || !@java.lang.reflect.Modifier@isStatic(method.method.getModifiers())'

选择的配置项的值

springContext.getEnvironment() (这个是要配置一个静态的spring context 看使用文档)

ognl -x 3 '#springContext=@com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider@context,#springContext.getEnvironment().getProperty("custom.name")' -c e374b99

获取所有的配置项的值

watch 获取spring context tt 、static 也是可以的哦~ 一样的原理

watch -x 3 -n 1  org.springframework.web.servlet.DispatcherServlet doDispatch '#springContext=@org.springframework.web.context.support.WebApplicationContextUtils@getWebApplicationContext(params[0].getServletContext()),#allProperties=,#standardServletEnvironment=#propertySourceIterator=#springContext.getEnvironment(),#propertySourceIterator=#standardServletEnvironment.getPropertySources().iterator(),#propertySourceIterator.#key=#this.getName(),#allProperties.add("                "),#allProperties.add("------------------------- name:"+#key),#this.getSource() instanceof java.util.Map ?#this.getSource().entrySet().iterator.#key=#this.key,#allProperties.add(#key+"="+#standardServletEnvironment.getProperty(#key)):#,#allProperties'

视频

有兴趣可以看一下视频~ 操作起来更流畅,基本上不用记忆啥。 arthas 入门到精通最佳实践

三、更多

还想了解更多关于arthas-idea-plugin 的内容可以联系我 可以通过 右键查看arthas-idea-help 找到代码地址和使用说明文档,更重要的是提一下好的idea 让arthas的使用更加的方便哦!插件地址: https://plugins.jetbrains.com/plugin/13581-arthas-idea

更多汪小哥

提问补充:

如何构造ognl的复杂入参,做了一个总结
ongl 逗号分隔,从前往后的执行执行不同的表达式;
全局参数,#开头为参数,ognl 脚本全局可以使用;
All OGNL expressions are evaluated in the context of a current object, and a chain simply uses the result of the previous link in the chain as the current object for the next one. You can extend a chain as long as you like. For example, this chain:

name.toCharArray()[0].numericValue.toString()

复杂参数的调用场景入这个arthas idea demo中所示 complexParameterCall 这个方法

@RequestMapping("complexParameterCall")
@ResponseBody
public String complexParameterCall(@RequestBody  Map<String, User> names) 
    if (names == null) 
        return "EMPTY";
    
    return names.toString();

默认情况下,插件对于复杂参数的处理策略只会生成简单的参数,这里只生成了Map 参数处理策略 太复杂的参数不太适用于对于线上问题的诊断,因此方法参数尽可能的简单,这里有一套规则,因为ognl的语法和Java类似的,在获取到参数的时候会进行默认的参数构造处理。 String ——> “” Number、Byte 、Char ——> 0 Long->OL Double->OD,Float->0F, Map ——> #"":" " ognl 语法 List ——> 数组 int[] ——>new int[],java.lang.Class->@java.lang.Object@class,other ——> new XXXClass()

默认情况下idea 插件生成为这个样子

ognl -x 3 '#springContext=@com.alibaba.lava.util.SpringUtil@context,#springContext.getBean("commonController").complexParameterCall(#" ":" ")' -c 41b801da

自己可以手动根据规则进行处理!
变量#user,设置值的信息,后续填充到map的参数里面去了,更多查看官方文档 或者中文版示范例子 ognl 使用姿势

ognl -x 3 '#user=new com.wangji92.arthas.plugin.demo.controller.User(),#user.setName("wangji"),#user.setAge(27L),#springContext=@com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider@context,#springContext.getBean("commonController").complexParameterCall(#"wangji":#user)' -c e374b99

阿里开源那个牛哄哄问题排查工具竟然不会用?最佳实践来了!(代码片段)

...is的第291篇原创分享作者l汪吉来源lHollis(ID:hollischuang)入门步骤安装https://arthas.gitee.io/install-detail.html上述命令会下载启动脚本文件as.sh到当前目录,执行方式:curl-Lhttps://aliba 查看详情

zabbix最佳实践二:快速入门(代码片段)

一.登录与配置用户1.1登陆这是Zabbix的“欢迎”界面。输入用户名Admin以及密码zabbix以作为Zabbix超级用户登陆。登陆后,你将会在页面右下角看到“以管理员连接(ConnectedasAdmin)”。同时会获得访问配置(Configuration)和管理(Admi... 查看详情

不容错过,零基础入门python学习路线最佳实践(代码片段)

今天我们来分享零基础入门Python,应该如何自学,自学的路径是怎么样的,内容是从入门到进阶,既有教程,也有经典书籍推荐,还有众多类库介绍,不要错过哦文章目录文档教程Python官方文档廖雪峰... 查看详情

arthas小白入门(代码片段)

 背景项目跑起来了,是否是按照预想的方式在跑,内存和CPU的占用情况,线程数等这些东西,该怎么监控查看。这个时候一般的人会找运维,现在有开源的了-arthas。Arthas数据展示 Arthas说明Arthas是Alibaba开源... 查看详情

es6+最佳入门实践(代码片段)

8.Promise8.1.什么是异步?要理解异步,首先,从同步代码开始说alert(1)alert(2)像上面的代码,执行顺序是从上到下,先后弹出1和2,这种代码叫做同步代码alert(0)setTimeout(function()alert(1);,2000);setTimeout(function()alert(2),1000);alert(3)上面代码... 查看详情

es6+最佳入门实践(11)(代码片段)

11.async函数async函数是什么?一句话,它就是Generator函数的语法糖。通俗的说就是Generator函数的另一种写法,这种写法更简洁,除此之外,async函数还对Genrator进行了一些改进首先,来回顾一下Generator函数实现文件读取constfs=require(... 查看详情

es6+最佳入门实践(10)(代码片段)

10.Generator10.1.Generator是什么?Generator函数是ES6提供的一种异步编程解决方案。在它的内部封装了多个状态,因此,又可以理解为一种状态机,执行Generator函数后返回一个迭代器对象,使用这个迭代器对象可以遍历出Generator函数内... 查看详情

es6+最佳入门实践(代码片段)

9.Iterator和for...of9.1.Iterator是什么?Iterator又叫做迭代器,它是一种接口,为各种不同的数据结构提供统一的访问机制。这里说的接口可以形象的理解为USB接口,有了这个接口可以做不同的事情,在编程中所说的接口最终都是要通... 查看详情

es6+最佳入门实践(12)(代码片段)

12.class基础用法和继承12.1.class基础语法在es5中,面向对象我们通常写成这样functionPerson(name,age)this.name=name;this.age=age;Person.prototype.showName=function()console.log(this.name);;letp=newPerson("xiaoqiang",10);p.showName();上面这种写法与传统的面... 查看详情

es6+最佳入门实践(代码片段)

7.set和map数据结构7.1.什么是set?Set就是集合,集合是由一组无序且唯一的项组成,在es6中新增了set这种数据结构,有点类似于数组,但是它的元素是唯一的,没有重复letst=newSet([1,2,2,3,3,4])console.log(st)//[1,2,3,4]Set的size属性可以知道se... 查看详情

es6+最佳入门实践(代码片段)

3.数组扩展3.1.扩展运算符扩展运算符用三个点(...)表示,从字面上理解,它的功能就是把数组扩展开来,具体形式如下:letarr=[1,2,3];console.log(...arr);//打印结果123//等价于console.log(1,2,3);从上面代码中,我们可以看出...arr展开后的形式... 查看详情

es6+最佳入门实践(代码片段)

6.Symbol用法6.1.什么是Symbol?Symbol是es6中一种新增加的数据类型,它表示独一无二的值。es5中我们把数据类型分为基本数据类型(字符串、数字、布尔、undefined、null)和引用数据类型(Object),在es6中新增的Symbol数据类型划分到基本数... 查看详情

es6+最佳入门实践(代码片段)

5.对象扩展5.1.对象简写在es5中,有这样一种写法varname="xiaoqiang";varage=12;varobj=name:name,age:age在es6中,我们可以简写成这样一种形式letname="xiaoqiang";letage=12;letobj=name,age以上只是属性的简写,如果有方法应该怎么写呢?... 查看详情

es6+最佳入门实践(13)(代码片段)

13.模块化13.1.什么是模块化模块化是一种处理复杂系统分解为更好的可管理模块的方式。通俗的讲就是把一个复杂的功能拆分成多个小功能,并且以一种良好的机制管理起来,这样就可以认为是模块化。就像作家把书分成很多章... 查看详情

es6+最佳入门实践(代码片段)

2.解构赋值2.1.什么是解构赋值?什么是解构赋值?这里的关键字还是赋值,这是说如何去赋值的问题,这里说的解构可以理解为解散重新构造,所以解构赋值可以理解为解散重新构造后进行赋值,通常是左边一种结构,右边一种... 查看详情

arthas(代码片段)

...ub.io/arthas/arthas-boot.jarjava-jararthas-boot.jarDocker通过Docker快速入门删除本地已有的arthas-demodockercontainer(非必要)$dockerstoparthas-demo||true&&dockerrmarthas-demo||true启动arthas-demo$dockerrun--namearthas-demo-ithengyunabc/arthas:latest/bin/sh-c"java-jar/opt/... 查看详情

javascript最佳实践(代码片段)

查看详情

markdowndevops最佳实践(代码片段)

查看详情