首页 > 编程语言 >Java 诊断工具 Arthas 常见命令(超详细实战教程)

Java 诊断工具 Arthas 常见命令(超详细实战教程)

时间:2023-07-24 09:01:03浏览次数:46  
标签:教程 Java demo Arthas user arthas java UserController com

基本概念

 

 

云原生这么多微服务,当然需要一个诊断利器来排查问题。

 

 

Arthas 是阿里开源的 Java 诊断工具,深受开发者喜爱。在线排查问题,无需重启;动态跟踪 Java 代码;实时监控 JVM 状态。Arthas 支持 JDK 6+,支持 Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

 

 

官方定义为Java应用诊断利器,截至目前github收获29.4K个star。

 

 

可以用来查看线程,内存,GC和运行时状态,查看入参/返回值/异常,快速定位应用的热点,生成火焰图等功能,帮助更快排查疑难问题。本文主要讲述常见命令的使用。

 

 

常见命令

 

 

启动arthas-demo(案例程序)

 

 

执行如下命令下载 arthas-demo.jar,再用java -jar命令启动案例程序:

 

 

wget https://arthas.aliyun.com/arthas-demo.jar;
java -jar arthas-demo.jar

 

 

启动arthas-boot(诊断工具程序)

 

 

执行如下命令下载arthas-boot.jar,再用java -jar命令启动:

 

 

wget https://arthas.aliyun.com/arthas-boot.jar;
java -jar arthas-boot.jar

 

 

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

 

 

选择要诊断的Java程序,我这里输入 1 ,再按回车键(Enter)。

 

 

Attach成功之后,会打印Arthas LOGO。

 

 

输入 help 可以获取到Arthas相关命令帮助信息。

 

 

[arthas@1266]$ help
NAME DESCRIPTION
help Display Arthas Help
auth Authenticates the current session
keymap Display all the available keymap for the specified connection.
sc Search all the classes loaded by JVM
sm Search the method of classes loaded by JVM
classloader Show classloader info
jad Decompile class
getstatic Show the static field of a class
monitor Monitor method execution statistics, e.g. total/success/failure count, average rt, fail
rate, etc.
stack Display the stack trace for the specified class and method
thread Display thread info, thread stack
trace Trace the execution time of specified method invocation.
watch Display the input/output parameter, return object, and thrown exception of specified me
thod invocation
tt Time Tunnel
jvm Display the target JVM information
memory Display jvm memory info.
perfcounter Display the perf counter information.
ognl Execute ognl expression.
mc Memory compiler, compiles java files into bytecode and class files in memory.
redefine Redefine classes. @see Instrumentation#redefineClasses(ClassDefinition...)
retransform Retransform classes. @see Instrumentation#retransformClasses(Class...)
dashboard Overview of target jvm's thread, memory, gc, vm, tomcat info.
dump Dump class byte array from JVM
heapdump Heap dump
options View and change various Arthas options
cls Clear the screen
reset Reset all the enhanced classes
version Display Arthas version
session Display current session information
sysprop Display, and change the system properties.
sysenv Display the system env.
vmoption Display, and update the vm diagnostic options.
logger Print logger info, and update the logger level
history Display command history
cat Concatenate and print files
base64 Encode and decode using Base64 representation
echo write arguments to the standard output
pwd Return working directory name
mbean Display the mbean information
grep grep command for pipes.
tee tee command for pipes.
profiler Async Profiler. https://github.com/jvm-profiling-tools/async-profiler
vmtool jvm tool
stop Stop/Shutdown Arthas server and exit the console.

 

 

与linux同样规则的命令此处不再赘述。如:history,cat,echo,pwd,grep。

 

 

系统的实时数据面板 dashboard 命令

 

 

dashboard 命令可以查看当前系统的实时数据面板。可以查看到CPU、内存、GC、运行环境等信息。

 

 

输入 q 或者 Ctrl+C 可以退出dashboard命令。

 

 

打印线程ID 的栈 thread

 

 

thread 1 命令会打印线程ID 1的栈。用 thread 1 | grep 'main(' 查找到main class。

 

 

查找JVM里已加载的类 sc/sm

 

 

可以通过 sc 命令来查找JVM里已加载的类,通过-d参数,可以打印出类加载的具体信息,很方便查找类加载问题。

 

 

[arthas@1266]$ sc -d *MathGame
class-info demo.MathGame
code-source /home/shell/arthas-demo.jar
name demo.MathGame
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name MathGame
modifier public
annotation
interfaces
super-class +-java.lang.Object
class-loader +-sun.misc.Launcher$AppClassLoader@1b6d3586
+-sun.misc.Launcher$ExtClassLoader@107df6e5
classLoaderHash 1b6d3586

Affect(row-cnt:1) cost in 50 ms.

 

 

sc支持通配,比如搜索所有的StringUtils:

 

 

sc *StringUtils

 

 

查找UserController的ClassLoader

 

 

[arthas@1266]$ sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash
classLoaderHash 19469ea2

 

 

sm命令则是查找类的具体函数。比如:

 

 

sm java.math.RoundingMode

 

 

通过-d参数可以打印函数的具体属性:

 

 

sm -d java.math.RoundingMode

 

 

查找特定的函数,比如查找构造函数:

 

 

sm java.math.RoundingMode <init>

 

 

反编译代码 jad命令

 

 

jad demo.MathGame

 

 

通过--source-only参数可以只打印出在反编译的源代码:

 

 

jad --source-only com.example.demo.arthas.user.UserController

 

 

动态执行代码 ognl 命令

 

 

在Arthas里,有一个单独的ognl命令,可以动态执行代码。这个有点秀啊

 

 

调用static函数

 

 

ognl '@[email protected]("hello ognl")'

 

 

获取静态类的静态字段

 

 

获取UserController类里的logger字段:

 

 

ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader @com.example.demo.arthas.user.UserController@logger

 

 

通过-x参数控制返回值的展开层数。比如:

 

 

ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -x 2 @com.example.demo.arthas.user.UserController@logger

 

 

执行多行表达式,赋值给临时变量,返回一个List

 

 

ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'

 

 

  • OGNL特殊用法请参考:https://github.com/alibaba/arthas/issues/71
  • OGNL表达式官方指南:https://commons.apache.org/proper/commons-ognl/language-guide.html

 

 

查看函数的参数/返回值/异常信息 watch 命令

 

 

watch demo.MathGame primeFactors returnObj

 

 

查看JVM信息 sysprop sysenv jvm dashboard

 

 

sysprop

 

 

  • sysprop :打印所有的System Properties信息。
  • 指定单个key:sysprop user.dir
  • 通过grep过滤 :sysprop | grep user
  • 设置新的value:sysprop testKey testValue

 

 

sysenv

 

 

sysenv 命令可以获取到环境变量。和sysprop命令类似。

 

 

jvm

 

 

jvm 命令会打印出JVM的各种详细信息。

 

 

dashboard

 

 

dashboard 命令可以查看当前系统的实时数据面板。

 

 

重置增强类 reset 命令

 

 

通过reset命令可以重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端关闭时会重置所有增强过的类。Arthas在 watch/trace 等命令时,实际上是修改了应用的字节码,插入增强的代码。显式执行 reset 命令,可以清除掉这些增强代码。

 

 

reset 还原指定类:

 

 

reset demo.MathGame

 

 

还原所有增强类:

 

 

reset

 

 

查看当前会话信息 session

 

 

tee 命令

 

 

类似传统的tee命令 用于读取标准输入的数据,并将其内容输出成文件。

 

 

tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。

 

 

查看当前Arthas版本 version

 

 

[arthas@1710]$ version
3.6.2

 

 

退出Arthas

 

 

输入 exit 或者 quit 命令可以退出Arthas当前session。执行 stop 命令彻底退出Arthas。

 

 

PS:所有命令都可以通过 -h 参数查看帮助信息。

 

 

实操案例

 

 

排查函数调用异常

 

 

通过curl 请求接口只能看到返回异常,但是看不到具体的请求参数和堆栈信息。

 

 

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

 

 

查看UserController的 参数/异常

 

 

在Arthas里执行:

 

 

watch com.example.demo.arthas.user.UserController * '{params, throwExp}'

 

 

  • 第一个参数是类名,支持通配
  • 第二个参数是函数名,支持通配 访问 curl http://localhost:61000/user/0 ,watch命令会打印调用的参数和异常

 

 

再次通过curl 调用可以在arthas里面查看到具体的异常信息。

 

 

把获取到的结果展开,可以用-x参数:

 

 

watch com.example.demo.arthas.user.UserController * '{params, throwExp}' -x 2

 

 

返回值表达式

 

 

在上面的例子里,第三个参数是返回值表达式,它实际上是一个ognl表达式,它支持一些内置对象:

 

 

  • loader
  • clazz
  • method
  • target
  • params
  • returnObj
  • throwExp
  • isBefore
  • isThrow
  • isReturn

 

 

比如返回一个数组:

 

 

watch com.example.demo.arthas.user.UserController * '{params[0], target, returnObj}'

 

 

条件表达式

 

 

watch命令支持在第4个参数里写条件表达式,比如:

 

 

  • 当访问 user/1 时,watch命令没有输出
  • 当访问 user/101 时,watch会打印出结果。

 

 

当异常时捕获

 

 

watch命令支持-e选项,表示只捕获抛出异常时的请求:

 

 

watch com.example.demo.arthas.user.UserController * "{params[0],throwExp}" -e

 

 

按照耗时进行过滤

 

 

watch命令支持按请求耗时进行过滤,比如:

 

 

watch com.example.demo.arthas.user.UserController * '{params, returnObj}' '#cost>200'

 

 

热更新代码

 

 

这个也是真的秀。

 

 

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

 

 

shell@Alicloud:~$ curl http://localhost:61000/user/0
{"timestamp":1655436218020,"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 窗口,然后用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

 

 

[arthas@1266]$ sc -d *UserController | grep classLoaderHash
classLoaderHash 19469ea2

 

 

classLoaderHash 是19469ea2,后面需要使用它。

 

 

mc

 

 

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

 

 

mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp

 

 

[arthas@1266]$ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 2879 ms.

 

 

也可以通过mc -c /tmp/UserController.java -d /tmp,使用-c参数指定ClassLoaderHash:

 

 

mc -c 19469ea2 /tmp/UserController.java -d /tmp

 

 

redefine

 

 

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

 

 

[arthas@1266]$ redefine /tmp/com/example/demo/arthas/user/UserController.class
redefine success, size: 1, classes:
com.example.demo.arthas.user.UserController

 

 

热修改代码结果

 

 

redefine成功之后,再次访问 user/0 ,结果正常

 

 

shell@Alicloud:~$ curl http://localhost:61000/user/0
{"id":0,"name":"name0"}

 

 

动态更新应用Logger Level

 

 

查找UserController的ClassLoader

 

 

[arthas@1266]$ sc -d *UserController | grep classLoaderHash
classLoaderHash 19469ea2

 

 

用ognl获取logger

 

 

ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader ‘@com.example.demo.arthas.user.UserController@logger’

 

 

[arthas@1266]$ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger'
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=null,
effectiveLevelInt=@Integer[20000],
parent=@Logger[Logger[com.example.demo.arthas.user]],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
]

 

 

可以知道UserController@logger实际使用的是logback。可以看到level=null,则说明实际最终的level是从root logger里来的。

 

 

单独设置UserController的logger level

 

 

ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@[email protected](@ch.qos.logback.classic.Level@DEBUG)'

 

 

再次获取UserController@logger,可以发现已经是DEBUG了。

 

 

修改logback的全局logger level

 

 

通过获取root logger,可以修改全局的logger level

 

 

ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@org.slf4j.LoggerFactory@getLogger("root").setLevel(@ch.qos.logback.classic.Level@DEBUG)'

 

 

获取Spring Context,在获取 bean,再调用函数

 

 

使用tt命令获取到spring context

 

 

tt即 TimeTunnel,它可以记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测。

 

 

官方tt说明:https://arthas.aliyun.com/doc/tt.html

 

 

tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod

 

 

访问user/1:

 

 

 curl http://localhost:61000/user/1

 

 

可以看到tt命令捕获到了一个请求:

 

 

输入 q 或者 Ctrl + C 退出上面的 tt -t命令。

 

 

使用tt命令从调用记录里获取到spring context

 

 

tt -i 1000 -w 'target.getApplicationContext()'

 

 

获取spring bean,并调用函数

 

 

tt -i 1000 -w ‘target.getApplicationContext().getBean(“helloWorldService”).getHelloMessage()’

 

 

结果如下:

 

 

[arthas@1266]$ tt -i 1000 -w 'target.getApplicationContext().getBean("helloWorldService").getHelloMessage()'
@String[Hello World]
Affect(row-cnt:1) cost in 1 ms.

 

 

排查HTTP请求返回401

 

 

请求接口没有权限的时候一般就返回401 Unauthorized。

 

 

401通常是被权限管理的Filter拦截了,那么到底是哪个Filter处理了这个请求,返回了401?

 

 

跟踪所有的Filter函数

 

 

开始trace:

 

 

trace javax.servlet.Filter *

 

 

可以在调用树的最深层,找到AdminFilterConfig$AdminFilter返回了401

 

 

+---[3.806273ms] javax.servlet.FilterChain:doFilter()
| `---[3.447472ms] com.example.demo.arthas.AdminFilterConfig$AdminFilter:doFilter()
| `---[0.17259ms] javax.servlet.http.HttpServletResponse:sendError()

 

 

通过stack获取调用栈

 

 

上面是通过trace命令来获取信息,从结果里,我们可以知道通过stack跟踪HttpServletResponse:sendError(),同样可以知道是哪个Filter返回了401

 

 

执行:

 

 

stack javax.servlet.http.HttpServletResponse sendError 'params[0]==401'

 

 

访问可以看到如下堆栈信息:

 

 

查找Top N线程

 

 

查看所有线程信息

 

 

thread

 

 

查看具体线程的栈

 

 

查看线程ID 2的栈:

 

 

thread 2

 

 

查看CPU使用率top n线程的栈

 

 

thread -n 3

 

 

查看5秒内的CPU使用率top n线程栈

 

 

thread -n 3 -i 5000

 

 

查找线程是否有阻塞

 

 

thread -b

 

 

更多使用查看:

 

 

  • Github地址: https://github.com/alibaba/arthas
  • 文档地址: https://arthas.aliyun.com/doc/

标签:教程,Java,demo,Arthas,user,arthas,java,UserController,com
From: https://www.cnblogs.com/lhxsoft/p/17576368.html

相关文章

  • java并发编程(一)----初识
    一、什么是并发  先看“科普中国”给出的官方解释。并发在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。   通俗来讲,并发就是指同一时间间隔内发生两个或多......
  • 数组去重方法总结(JavaScript 记录)
    在进行项目开发的时候,有时候需要把一些前端的数组进行去重处理,得到一个去重后的数据,然后再进行相关的操作,这也是在前端面试中经常出现的问题数组去重的多种方法:利用ES6Set去重利用for嵌套for,然后splice去重利用indexOf去重利用sort()去重利用对象的属性不能相......
  • 入门篇-其之一-第一个Java程序
    ⚠️注意:本文中包含实际操作,需要安装JDK。如果需要安装JDK,请按照这篇文章的步骤进行安装:点我查看JDK安装教程小白可以多看几遍这篇文章,多敲几次代码前面我们已经安装了JDK,接下来就是写一个属于自己的Java程序了。用什么写自己的Java程序呢?有人说用IDEA、Eclipse等集成开发工......
  • Java基础复习—— IO流2
    IO流节点流和处理流节点流可以从一个特点的数据源读写数据,如FileReader、FileWriter处理流(也叫包装流)是“连接”在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,也更加灵活,如BufferedReader、.BufferedWriter区别和联系节点流是底层流(低级流),直接和数据源......
  • java深浅拷贝
    对于Java拷贝的理解在java语言中,当我们需要拷贝一个对象的时候,常见的会有两种方式的拷贝:深拷贝和浅拷贝。浅拷贝只是拷贝了原对象的地址,所以原对象的任何值发生改变的时候,拷贝对象的值也会随之而发生变化。拿地址。深拷贝则是拷贝源对象的所有值而不是地址,所以即源......
  • 牛客周赛Round4(java)
     Java组代码importjava.util.Scanner;publicclassMain{publicstaticvoidmain(String[]args){Scannerscanner=newScanner(System.in);intn=scanner.nextInt();intm=scanner.nextInt();StringBuildersb=newStringB......
  • Java服务刚启动时,一小波接口超时排查全过程
    原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,非公众号转载保留此声明。简介我们组有一个流量较大的Java服务,每次发代码时,服务都会有一小波接口超时,之前简单分析过,发现这些超时的case仅发生在服务刚启动时,少量请求会耗时好几秒,但之后又马上恢复正常。问题发生如下,是我们服务的......
  • Shell编程教程 - 字符串变量表达式
    1.字符串变量表达式基本比较示例脚本-`string_comparison.sh`字符串长度检查示例脚本-`string_length.sh`逻辑运算符连接表达式示例脚本-`logical_operators.sh`2.执行示例脚本3.结论大树哥个人信息本教程将向你介绍Shell脚本中字符串变量表达式的使用。我们将学习如何......
  • 如何将jsp中的值传到java代码中
    在JSP中,我们可以使用Java代码与HTML代码相结合,实现动态网页的开发。有时候我们需要将JSP页面中的值传递到后台的Java代码中进行处理,这时候可以通过以下几种方式实现。使用表单提交数据:我们可以在JSP页面中使用HTML的表单标签,通过表单的提交将值传递到后台的Java代码中。下面是一......
  • 协同过滤算法java
    协同过滤算法及其在Java中的应用协同过滤算法是一种常用的推荐系统算法,通过分析用户行为和偏好,找出用户之间的相似性,从而向用户推荐相关的物品或内容。本文将介绍协同过滤算法的原理,并使用Java语言实现一个简单的协同过滤算法示例。协同过滤算法原理协同过滤算法基于一个假设:如......