Arthas

Arthas简介

Arthas 是Alibaba开源的Java诊断工具,安装在应用服务器,可帮助开发人员和运维人员排查问题,深受开发者喜爱。
github地址: https://alibaba.github.io/arthas/

Arthas解决的问题

当遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

  • 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  • 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  • 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  • 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  • 是否有一个全局视角来查看系统的运行状况?
  • 有什么办法可以监控到JVM的实时运行状态?

Arthas支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

安装使用

使用arthas-boot(推荐)

下载arthas-boot.jar,然后用java -jar的方式启动:

# 下载arthas-boot.jar
wget https://alibaba.github.io/arthas/arthas-boot.jar
# 复制安装包到你认为合适的地方
cp ./arthas-boot.jar /tmp
# 启动命令
java -jar arthas-boot.jar
# 打印帮助信息:
java -jar arthas-boot.jar -h
  • 如果下载速度比较慢,可以使用aliyun的镜像:java -jar arthas-boot.jar --repo-mirror aliyun --use-http

使用as.sh

Arthas 支持在 Linux/Unix/Mac 等平台上一键安装,请复制以下内容,并粘贴到命令行中,敲 回车 执行即可:

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

上述命令会下载启动脚本文件 as.sh 到当前目录,你可以放在任何地方或将其加入到 $PATH 中。直接在shell下面执行./as.sh,就会进入交互界面。也可以执行./as.sh -h来获取更多参数信息。

命令介绍

下载arthas-demo.jar,再用java -jar命令启动:

`wget https://alibaba.github.io/arthas/arthas-demo.jar java -jar arthas-demo.jar`

arthas-demo是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。
在新的Terminal 2里,下载arthas-boot.jar,再用java -jar命令启动:

`wget https://alibaba.github.io/arthas/arthas-boot.jar java -jar arthas-boot.jar --target-ip 0.0.0.0`

arthas-bootArthas的启动程序,它启动后,会列出所有的Java进程,用户可以选择需要诊断的目标进程。

选择第一个进程,输入 1 ,再Enter/回车
Attach成功之后,会打印Arthas LOGO。输入 help 可以获取到更多的帮助信息。

arthas-boot.png

Dashboard

dashboard 命令可以查看当前系统的实时数据面板。输入 Q 或者 Ctrl+C 可以退出dashboard命令。

dashboard

Thread

thread 1 命令会打印线程ID 1的栈。
Arthas支持管道,可以用 thread 1 | grep 'main(' 查找到main class。可以看到main classdemo.MathGame

$ thread 1 | grep 'main('at demo.MathGame.main(MathGame.java:17)

sc (search class)

可以通过 sc 命令来查找JVM里已加载的类:

sc -d *MathGame

mc (memory compile)

可以通过mc命令编译指定的java类

mc -c 327a647b /tmp/Test.java

Jad

可以通过 jad 命令来反编译代码:

jad demo.com/guanyi/dto/aiot/AiotHttpHelper

Watch

通过watch命令可以查看函数的参数/返回值/异常信息。

watch demo.MathGame primeFactors returnObj

输入 Q 或者 Ctrl+C 退出watch命令。

Exit/Shutdown

exit 或者 quit 命令可以退出Arthas。
退出Arthas之后,还可以再次用 java -jar arthas-boot.jar 来连接。

彻底退出Arthas

exit/quit命令只是退出当前session,arthas server还在目标进程中运行。

想完全退出Arthas,可以执行 shutdown 命令。

使用案例一:排查函数调用异常现象

目前,访问 http://localhost/user/0 ,会返回500异常:

查看UserController的 参数/异常

$ watch com.example.demo.arthas.user.UserController * '{params, throwExp}' -x 2 
Press Q or Ctrl+C to abort. 
Affect(class-cnt:1 , method-cnt:2) cost in 53 ms. 
ts=2019-02-15 01:35:25; [cost=0.996655ms] result=@ArrayList[  
  @Object[][isEmpty=false;size=1], 
  @IllegalArgumentException[java.lang.IllegalArgumentException: id < 1], 
]
image.png

案例二: 热更新代码

下面介绍通过jad/mc/redefine 命令实现动态更新代码的功能。
目前,访问 http://localhost/user/0 ,会返回500异常:

$ curl http://localhost/user/0
{"timestamp":1550223186170,"status":500,"error":"Internal Server Error","exception":"java.lang.IllegalArgumentException","message":"id < 1","path":"/user/0"}

下面通过热更新代码,修改这个逻辑。

jad反编译UserController

jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java

jad反编译的结果保存在 /tmp/UserController.java文件里了。

再打开一个Terminal 3,然后用vim来编辑/tmp/UserController.java

vim /tmp/UserController.java

比如当 user id 小于1时,也正常返回,不抛出异常:

@GetMapping(value={"/user/{id}"})  
public User findUserById(@PathVariable Integer id) 
{ 
    logger.info("id: {}", (Object)id); 
    if (id != null && id < 1) {
         return new User(id, "name" + id); 
        // throw new IllegalArgumentException("id < 1"); 
   } 
   return new User(id.intValue(), "name" + id);
 }

sc查找加载UserController的ClassLoader

# 查看classloader sc -d *UserController | grep classLoaderHash

$ sc -d *UserController | grep classLoaderHash 
  classLoaderHash   1be6f5c3

可以发现是 spring boot LaunchedURLClassLoader@1be6f5c3 加载的。

mc

保存好/tmp/UserController.java之后,使用mc(Memory Compiler)命令来编译,并且通过-c参数指定ClassLoader:

# 编译目标class mc -c 1be6f5c3 /tmp/UserController.java -d /tmp

$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp 
Memory compiler output: 
/tmp/com/example/demo/arthas/user/UserController.class 
Affect(row-cnt:1) cost in 346 ms

redefine

使用redefine命令重新加载新编译好的UserController.class

# 导入class到内存 redefine /tmp/com/example/demo/arthas/user/UserController.class

$ redefine /tmp/com/example/demo/arthas/user/UserController.class 
redefine success, size: 

热修改代码结果

redefine成功之后,再次访问 https://2886795400-80-cykoria03.environments.katacoda.com/user/0 ,结果是:

{  "id": 0, "name": "name0" }

案例展示

Dashboard(仪表盘)

  • https://alibaba.github.io/arthas/dashboard
  • dashboard.png

Thread

一目了然的了解系统的状态,哪些线程比较占cpu?他们到底在做什么?

$ thread -n 3
"as-command-execute-daemon" Id=29 cpuUsage=75% RUNNABLE
    at sun.management.ThreadImpl.dumpThreads0(Native Method)
    at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:440)
    at com.taobao.arthas.core.command.monitor200.ThreadCommand$1.action(ThreadCommand.java:58)
    at com.taobao.arthas.core.command.handler.AbstractCommandHandler.execute(AbstractCommandHandler.java:238)
    at com.taobao.arthas.core.command.handler.DefaultCommandHandler.handleCommand(DefaultCommandHandler.java:67)
    at com.taobao.arthas.core.server.ArthasServer$4.run(ArthasServer.java:276)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

    Number of locked synchronizers = 1
    - java.util.concurrent.ThreadPoolExecutor$Worker@6cd0b6f8

"as-session-expire-daemon" Id=25 cpuUsage=24% TIMED_WAITING
    at java.lang.Thread.sleep(Native Method)
    at com.taobao.arthas.core.server.DefaultSessionManager$2.run(DefaultSessionManager.java:85)

"Reference Handler" Id=2 cpuUsage=0% WAITING on java.lang.ref.Reference$Lock@69ba0f27
    at java.lang.Object.wait(Native Method)
    -  waiting on java.lang.ref.Reference$Lock@69ba0f27
    at java.lang.Object.wait(Object.java:503)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)

jad

对类进行反编译:

$ jad javax.servlet.Servlet

ClassLoader:
+-java.net.URLClassLoader@6108b2d7
  +-sun.misc.Launcher$AppClassLoader@18b4aac2
    +-sun.misc.Launcher$ExtClassLoader@1ddf84b8

Location:
/Users/xxx/work/test/lib/servlet-api.jar

/*
 * Decompiled with CFR 0_122.
 */
package javax.servlet;

import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public interface Servlet {
    public void init(ServletConfig var1) throws ServletException;

    public ServletConfig getServletConfig();

    public void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    public String getServletInfo();

    public void destroy();
}

sc

查找JVM中已经加载的类

$ sc -d org.springframework.web.context.support.XmlWebApplicationContext
 class-info        org.springframework.web.context.support.XmlWebApplicationContext
 code-source       /Users/xxx/work/test/WEB-INF/lib/spring-web-3.2.11.RELEASE.jar
 name              org.springframework.web.context.support.XmlWebApplicationContext
 isInterface       false
 isAnnotation      false
 isEnum            false
 isAnonymousClass  false
 isArray           false
 isLocalClass      false
 isMemberClass     false
 isPrimitive       false
 isSynthetic       false
 simple-name       XmlWebApplicationContext
 modifier          public
 annotation
 interfaces
 super-class       +-org.springframework.web.context.support.AbstractRefreshableWebApplicationContext
                     +-org.springframework.context.support.AbstractRefreshableConfigApplicationContext
                       +-org.springframework.context.support.AbstractRefreshableApplicationContext
                         +-org.springframework.context.support.AbstractApplicationContext
                           +-org.springframework.core.io.DefaultResourceLoader
                             +-java.lang.Object
 class-loader      +-org.apache.catalina.loader.ParallelWebappClassLoader
                     +-java.net.URLClassLoader@6108b2d7
                       +-sun.misc.Launcher$AppClassLoader@18b4aac2
                         +-sun.misc.Launcher$ExtClassLoader@1ddf84b8
 classLoaderHash   25131501

stack

查看方法 test.arthas.TestStack#doGet 的调用堆栈:可查看到每个方法的调用耗时

trace.png

Monitor

监控某个特殊方法的调用统计数据,包括总调用次数,平均rt,成功率等信息,每隔5秒输出一次。

$ monitor -c 5 org.apache.dubbo.demo.provider.DemoServiceImpl sayHello
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 109 ms.
 timestamp            class                                           method    total  success  fail  avg-rt(ms)  fail-rate
----------------------------------------------------------------------------------------------------------------------------
 2018-09-20 09:45:32  org.apache.dubbo.demo.provider.DemoServiceImpl  sayHello  5      5        0     0.67        0.00%

 timestamp            class                                           method    total  success  fail  avg-rt(ms)  fail-rate
----------------------------------------------------------------------------------------------------------------------------
 2018-09-20 09:45:37  org.apache.dubbo.demo.provider.DemoServiceImpl  sayHello  5      5        0     1.00        0.00%

 timestamp            class                                           method    total  success  fail  avg-rt(ms)  fail-rate
----------------------------------------------------------------------------------------------------------------------------
 2018-09-20 09:45:42  org.apache.dubbo.demo.provider.DemoServiceImpl  sayHello  5      5        0     0.43        0.00%

Classloader

了解当前系统中有多少类加载器,以及每个加载器加载的类数量,帮助您判断是否有类加载器泄露。

$ classloader
name                                                  numberOfInstances  loadedCountTotal
BootstrapClassLoader                                  1                  3346
com.taobao.arthas.agent.ArthasClassloader             1                  1262
java.net.URLClassLoader                               2                  1033
org.apache.catalina.loader.ParallelWebappClassLoader  1                  628
sun.reflect.DelegatingClassLoader                     166                166
sun.misc.Launcher$AppClassLoader                      1                  31
com.alibaba.fastjson.util.ASMClassLoader              6                  15
sun.misc.Launcher$ExtClassLoader                      1                  7
org.jvnet.hk2.internal.DelegatingClassLoader          2                  2
sun.reflect.misc.MethodUtil                           1                  1

你可能感兴趣的:(Arthas)