前言
Arthas
是 Alibaba 开源的 Java 诊断工具。在线排查问题,无需重启;动态跟踪 Java 代码;实时监控 JVM 状态。对分秒必争的线上异常,Arthas
可帮助我们快速诊断相关问题。
下载安装
下载Arthas
的arthas-boot.jar
wget https://alibaba.github.io/arthas/arthas-boot.jar
复制代码
下载arthas
之后,先来了解帮助信息,可以通过java -jar arthas-boot.jar -h
命令查看,这里给出了一些例子和参数说明
[root@izwz94a0v1sz0gk4rezdcbz arthas]# java -jar arthas-boot.jar -h
[INFO] arthas-boot version: 3.1.4
Usage: arthas-boot [-h] [--target-ip <value>] [--telnet-port <value>]
[--http-port <value>] [--session-timeout <value>] [--arthas-home <value>]
[--use-version <value>] [--repo-mirror <value>] [--versions] [--use-http]
[--attach-only] [-c <value>] [-f <value>] [--height <value>] [--width
<value>] [-v] [--tunnel-server <value>] [--agent-id <value>] [--stat-url
<value>] [pid]
Bootstrap Arthas
EXAMPLES:
java -jar arthas-boot.jar <pid>
java -jar arthas-boot.jar --target-ip 0.0.0.0
java -jar arthas-boot.jar --telnet-port 9999 --http-port -1
java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws'
java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws'
--agent-id bvDOe8XbTM2pQWjF4cfw
java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'
java -jar arthas-boot.jar -c 'sysprop; thread' <pid>
java -jar arthas-boot.jar -f batch.as <pid>
java -jar arthas-boot.jar --use-version 3.1.4
java -jar arthas-boot.jar --versions
java -jar arthas-boot.jar --session-timeout 3600
java -jar arthas-boot.jar --attach-only
java -jar arthas-boot.jar --repo-mirror aliyun --use-http
WIKI:
https://alibaba.github.io/arthas
Options and Arguments:
-h,--help Print usage
--target-ip <value> The target jvm listen ip, default 127.0.0.1
--telnet-port <value> The target jvm listen telnet port, default 3658
--http-port <value> The target jvm listen http port, default 8563
--session-timeout <value> The session timeout seconds, default 1800
(30min)
--arthas-home <value> The arthas home
--use-version <value> Use special version arthas
--repo-mirror <value> Use special maven repository mirror, value is
center/aliyun or http repo url.
--versions List local and remote arthas versions
--use-http Enforce use http to download, default use https
--attach-only Attach target process only, do not connect
-c,--command <value> Command to execute, multiple commands separated
by ;
-f,--batch-file <value> The batch file to execute
--height <value> arthas-client terminal height
--width <value> arthas-client terminal width
-v,--verbose Verbose, print debug info.
--tunnel-server <value> The tunnel server url
--agent-id <value> The agent id register to tunnel server
--stat-url <value> The report stat url
<pid> Target pid
复制代码
启动
启动arthas
之前,先启动一个springboot
的应用。该demo
在地址https://github.com/yangtao...
java -jar ytao-springboot-demo.jar
复制代码
启动arthas-boot.jar
命令
java -jar arthas-boot.jar
复制代码
这里注意需要启动demo
和arthas
使用同一权限用户,否则使用 attach 机制获取不到进程信息(这里刚使用时没注意,遇到过这个问题)。例:root
用户启动 demo
,u1
用户启动arthas
时,打印信息Can not find java process. Try to pass <pid> in command line.
查看源码,在获取进程之后,添加日志输出。结果为空,返回-1
,判断结果小于0
时,直接退出。
启动类Bootstrap#main
的代码
进程工具类ProcessUtils#select
的代码
通过上面也分析到,我们启动arthas
之前,必须要先启动我们的目标进程,否则arthas
可能无法启动。
使用root
用户启动成功界面
选择 java 进程,这里我们的ytao-springboot-demo
是 1,选择后会有连接信息
[INFO] arthas home: /root/.arthas/lib/3.1.4/arthas
[INFO] Try to attach process 22005
[INFO] Attach process 22005 success.
[INFO] arthas-client connect 127.0.0.1 3658
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'
wiki https://alibaba.github.io/arthas
tutorials https://alibaba.github.io/arthas/arthas-tutorials
version 3.1.4
pid 17339
time 2019-10-17 02:29:06
复制代码
dashboard 数据面板
使用dashboard
命令,可以查看线程,内存,GC,以及 Runtime 信息
jad 反编译
有时我们会遇到线上代码运行结果不是我们期望的结果,有种情况就是线上代码不是我们想要的版本,但是要查看的话,需要下载后再进行反编译。这时arthas
的jad
可以帮助我们线上进行即时反编译,确认代码是否符合我们的版本。
jad com.ytao.service.UserServiceImpl
复制代码
jad 反编译
watch 函数执行信息
使用watch
命令可以查看函数的执行信息。watch
的参数列表(来自官网)
参数参数说明 class-pattern 类名表达式匹配 method-pattern 方法名表达式匹配 express 观察表达式 condition-express 条件表达式[b]在方法调用之前观察[e]在方法异常之后观察[s]在方法返回之后观察[f]在方法结束之后(正常返回和异常返回)观察[E]开启正则表达式匹配,默认为通配符匹配[x:]指定输出结果的属性遍历深度,默认为 1
当我们遇到线上数据bug
时,我们一般处理的手段就是开发环境模拟线上数据,从生产日志中查找线索,再或者远程debug
。以上不管哪种排查手段,相对都是比较麻烦。这时 Arthas 的watch
可以帮助我们查看实时的代码执行情况。使用观察表达式可以查看函数的参数
,返回值
,异常信息
。观察表达式主要由OGNL
表达式组成,所以可以编写OGNL
表达式来执行。
观察表达式的变量
变量变量说明 params 函数的入参 returnObj 函数的返回值 throwExp 异常信息 target 当前对象
查看一个函数的入参和返回值
watch com.ytao.service.UserServiceImpl getUser "{params,returnObj}"
复制代码
watch 命令的参数和返回值信息
打印信息isEmpty=false;size=1
可以看到参数为非空,参数数量为一个。查看具体入参信息
watch com.ytao.service.UserServiceImpl getUser "{params[0],returnObj}"
复制代码
watch 命令的具体的入参信息
查看异常信息
watch com.ytao.service.UserServiceImpl getUser "throwExp"
复制代码
当我们传入一个参数为-1
时,打印出我们定义的非法参数异常
watch
除了观察表达式外,还能使用条件表达式
,以及观察事件点
。注意使用观察事件点时,有些观察表达式的变量不一定存在,比如使用-b
时,返回值和异常信息都为空。
有时我们排查某个函数,不能马上获取到函数的信息,arthas
给提供的后台异步任务
可以帮助我们记录日志。使用方式和 Linux 的类似。
watch com.ytao.service.UserServiceImpl getUser "{params,returnObj}" > /log/w.log &
复制代码
查看异步保存的日志
tt 定位异常调用
上面所介绍的watch
可以排查函数的调用情况,比较适用在已知当次调用可能存在的情况后,查看信息。如果一个函数调用 n 次后,有几次为执行异常,我们要去找出这些异常的调用,在watch
中排查就不怎么方便了。使用tt
命令可以较方便查看异常的调用及信息。对com.ytao.service.UserServiceImpl#getUser
的函数查看,-t
是每次调用该函数都会记录
tt -t com.ytao.service.UserServiceImpl getUser
复制代码
记录信息
查看所有记录
查看指定函数记录
tt -s 'method.name=="getUser"'
复制代码
输出信息说明
表格字段字段解释 INDEX 时间片段记录编号,每一个编号代表着一次调用,后续 tt 还有很多命令都是基于此编号指定记录操作,非常重要。TIMESTAMP 方法执行的本机时间,记录了这个时间片段所发生的本机时间 COST(ms)方法执行的耗时 IS-RET 方法是否以正常返回的形式结束 IS-EXP 方法是否以抛异常的形式结束 OBJECT 执行对象的 hashCode(),注意,曾经有人误认为是对象在 JVM 中的内存地址,但很遗憾他不是。但他能帮助你简单的标记当前执行方法的类实体 CLASS 执行的类名 METHOD 执行的方法名
从上面参数中我们看到1003
调用是以抛异常的形式结束,因为tt
会记录每次调用的信息,所以我们可以查看1003
的详细信息
tt 调用详细
trace 查看调用链路
我们常会遇到调用某个 api 时 rt 过长,我们就要找出调用链上的某个或几个函数进行优化,我们通常定位几个可能的锚点,打印各个锚点间的 rt。或者从日志中找出日志打印的时间点计算出时间差,不管使用哪种方法都比较繁琐。当使用arthas
的trace
命令可以轻松的完成我们的需求。trace
参数说明
参数参数说明 class-pattern 类名表达式匹配 method-pattern 方法名表达式匹配 condition-express 条件表达式[E]开启正则表达式匹配,默认为通配符匹配[n:]命令执行次数 #cost 方法执行耗时
使用trace
输出com.ytao.controller.UserController#getUser
的信息
trace com.ytao.service.UserServiceImpl getUser
复制代码
输出结果
在实际使用使用排查过程中,为了减少无用信息的输出,我们一般会使用#cost
过滤耗时不长和 jdk 自带的函数,可以忽略的调用,减少信息的输出。例如:过滤掉小于1ms
的调用
trace com.ytao.service.UserServiceImpl getUser '#cost > 1'
复制代码
redefine 实现热部署
当我们查找出 bug,想要快速上线拯救苍生的时候,Arthas
为我们准备了redefine
命令来实现热更新。尽管现在都在大赞jad
/mc
/redefine
热更一条龙,但是线上代码建议本地编译好后再进行替换,避免手误操作。首先先在UserServiceImpl
中添加一行代码
获取classLoaderHash
,通过sc
命令获取类的信息
classLoaderHash
执行redefine
修改的类
redefine -c 1d56ce6a /usr/local/jar/UserServiceImpl.class
复制代码
通过打印的信息验证是否更新UserServiceImpl
类
Arthas
的使用,除了上文中所讲解到的,还有一些其他的诊断功能,这只是我个人使用的方法。但是使用该类工具一定要有套组合拳,对排查问题过程中,遇到问题有对应的排查手段,并非盲目排查。
推荐阅读
《Dubbo 路由机制的实现》
《Dubbo 扩展点加载机制:从 Java SPI 到 Dubbo SPI》
《Dubbo之服务消费原理》
《Dubbo之服务暴露》
《你必须会的 JDK 动态代理和 CGLIB 动态代理》
《Java 线程通信之 wait/notify 机制》
《Netty中粘包/拆包处理》
评论