Arthas的实用操作,以下操作都是在进去arthas命令行之后操作的,以下操作在rainbow应用上进行。

1. 查看指定类的加载信息,还能看被Spring增强的类信息

sc -d com.pugwoo.rainbow.bookmark.web.BookmarkController # -d是显示详细信息
# 可以用匹配符 sc -d *BookmarkController*

可以看到的信息:

  1. 如果这个类是被Spring容器增加过的,一般可以看到一个原始的类和一个被CGLIB增强的类。
  2. 可以看到这个类是从哪个文件加载的,例如排查版本冲突时,就可以看被加载的jar包的版本对不对。
  3. 可以看它是从哪个classloader加载的,在java中,不同classloader加载的类是属于不同的类,NoClassDefine异常要注意看看找不到的类是不是在不同的classloader中。

2. 反编译指定的类

jad com.pugwoo.rainbow.bookmark.web.BookmarkController

这个功能可以看代码是不是最新的,也方便当本地没有代码时,看异常堆栈对应的代码。

3. 跟踪指定方法的下游调用链及耗时(支持spring增加后的bean)

trace com.pugwoo.rainbow.bookmark.web.BookmarkController query

然后再触发一下这个方法的调用(例如访问一下页面),此时可以看到输出:

`---ts=2020-08-24 10:48:20;thread_name=http-nio-8080-exec-3;id=20;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@7ee8290b
    `---[127.071652ms] com.pugwoo.rainbow.bookmark.web.BookmarkController$$EnhancerBySpringCGLIB$$62f2797f:query()
        `---[126.528791ms] org.springframework.cglib.proxy.MethodInterceptor:intercept()
            `---[125.472419ms] com.pugwoo.rainbow.bookmark.web.BookmarkController:query()
                +---[0.092069ms] org.apache.commons.lang.StringUtils:isNotEmpty() #49
                +---[98.372755ms] com.pugwoo.rainbow.bookmark.service.IBookmarksService:getList() #57
                +---[0.053562ms] com.pugwoo.admin.utils.PageUtils:trans() #60
                +---[26.269881ms] com.pugwoo.rainbow.bookmark.service.IBookmarksTagService:getTagCount() #62
                `---[0.049894ms] com.pugwoo.admin.bean.WebJsonBean:ok() #65

可以看出,这个调用的时间和被哪个线程执行,是哪个对象执行的,类加载器是什么。整个调用的耗时,以及它内部每个调用的耗时(这里有个缺点就是只能看一层,如果要跟踪下层,就只能手工在trace下层的方法)

上次还碰到一个问题,访问tomcat的url没反应,我们想先确定下是请求到了tomcat没处理,还是压根就没到tomcat(可能监听端口都没了),那么可以trace javax.servlet.http.HttpServlet service,正常情况下有请求就会进来。

4. 和trace方向相反,跟踪指定方法被谁调用了(支持spring增加后的bean)

stack com.pugwoo.rainbow.bookmark.web.BookmarkController query

输出:

ts=2020-08-24 10:57:35;thread_name=http-nio-8080-exec-7;id=24;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@7ee8290b
    @com.pugwoo.rainbow.bookmark.web.BookmarkController.query()
        at com.pugwoo.rainbow.bookmark.web.BookmarkController$$FastClassBySpringCGLIB$$4ec2f760.invoke(<generated>:-1)
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:685)
        at com.pugwoo.rainbow.bookmark.web.BookmarkController$$EnhancerBySpringCGLIB$$62f2797f.query(<generated>:-1)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888)

还有一个实际例子是看看谁主动调了System.gc() :https://github.com/alibaba/arthas/issues/20

5. 查看指定方法的调用的入参和返回值

watch com.pugwoo.rainbow.bookmark.web.BookmarkController  query  "{params,returnObj}"  -x 2
# -x应该是显示的深度,如果不指定-x 2,会显示出所有结果,但比较慢

输出:

ts=2020-08-24 11:02:56; [cost=104.235079ms] result=@ArrayList[
    @Object[][isEmpty=false;size=7],
    @WebJsonBean[WebJsonBean(code=0, msg=成功, data={total=0, data=[], pageSize=10, tagCount={}})],
]

6. 统计指定方法的调用时间

例如上面的方法,一定时间频率去采样,看看结果怎样:

monitor -c 5 com.pugwoo.rainbow.bookmark.web.BookmarkController  query
# -c是每5秒一次

输出结果:

timestamp         class                      method                     total    success  fail     avg-rt(  fail-ra 
                                                                                                    ms)      te      
---------------------------------------------------------------------------------------------------------------------
 2020-08-24 11:05  com.pugwoo.rainbow.bookma  query                      3        3        0        28.30    0.00%   
 :09               rk.web.BookmarkController                                                                         
 2020-08-24 11:05  com.pugwoo.rainbow.bookma  query                      3        3        0        28.40    0.00%   
 :09               rk.web.BookmarkController                                                                         
                   $$EnhancerBySpringCGLIB$$                                                                         

7. 拿到Spring容器里面的bean并对其进行调用

首先项目中需要先把Spring容器applicationContext对象保存在一个类的静态成员上,这个很容易做,但需要先做。例如在admin-template中,就保存在com.pugwoo.admin.SpringContext类中。(当然还有一种不用通过事先做这个代码来做到的,但是也比较麻烦,需要tt先抓请求记录,再从请求记录中去拿applicationContext)。

sc -d com.pugwoo.admin.SpringContext # 看下这个类是否存在,已经最重要的,它的classLoaderHash
ognl -c 它的classLoaderHash '@com.pugwoo.admin.SpringContext@context.getBean("bookmarkController")'

上面例子就拿到了bookmarkController的bean,然后可以继续写下去调用它的方法。

8. 对请求进行重放

TODO tt命令,参考下面的文章,还没实验

参考文章

  1. https://juejin.im/post/6844903765145813006
文档更新时间: 2020-08-26 16:00   作者:nick