文章目录
🌞导读
我在面试时通常都会问求职者的一个问题:你如何定位线上程序cpu飙升,内存占用过高,出现死锁的问题。我发现很多工作了多年的开发者都不知道如何排查线上的问题,甚至没有相关排查经验,出现问题时更快速解决的方案可能是重启,然后在本地尝试复现问题,打断点看。
你可能会告诉我,可以远程debug,jdk也为我们提供了jsp、jstat、jmap、jstack、javap一系列工具都可以帮助我们排查问题呀,远程debug会导致线上应用整体阻塞,而jdk自带命令用过的小伙伴肯定会觉得这些命令难以操作,且定位也比较困难。今天我就教大家如何使用Arthas
这款神器来快速精准的定位线上问题。
📖Arthas介绍
Arthas是阿里巴巴开源的Java诊断工具,其本身也是一个java程序,可采用命令行与其交互,可方便快速的定位线上运行的问题,深受各大公司开发人员喜爱。
那么它能干什么呢
- 可视化控制台,直观的观测到程序的运行情况
- 局部反编译,可具体到方法进行反编译操作
- 对方法中每一行代码的执行时长进行监控
- 查看JVM的实时状态
- 快速定位热点线程以及他占用cpu比例
- 查看方法的出参入参
- 直接执行某一段代码
- 。。。
看到这里,你可能已经跃跃欲试了,接下来让我们试试它到底有多强大吧。
⏬Arthas如何使用
使用方式很简单,我们直接执行以下命令将jar包下载下来即可
# 下载地址一 github下载
wget https://alibaba.github.io/arthas/arthas-boot.jar
# 下载地址二 gitee下载
wget https://arthas.gitee.io/arthas-boot.jar
✅运行
java -jar arthas-boot.jar [pid]
# 输入pid编号 选择我们需要监控的程序
[INFO] arthas-boot version: 3.5.4
[INFO] Process 3492 already using port 3658
[INFO] Process 3492 already using port 8563
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 3492 org.jetbrains.idea.maven.server.RemoteMavenServer36
[2]: 5904
[3]: 8280 com.example.demo.DemoApplication
这时候可以看到我们有3个jvm进程,输入相应进程号回车
看到如下信息时,我们就可以使用Arthas命令来进行监控了
[INFO] The target process already listen port 3658, skip attach.
[INFO] arthas-client connect 127.0.0.1 3658
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'
wiki https://arthas.aliyun.com/doc
tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html
version 3.5.4
main_class
pid 8280
time 2021-10-27 22:29:37
可以看到控制台信息,arthas提供了一个3658的端口号,我们可直接通过浏览器访问http://127.0.0.1:3658,就可以进入到web页面,页面的操作与控制台相同。
⭐️常用命令
命令 | 描述 |
---|---|
dashboard | 当前系统的实时面板 |
thread | 查看当前 JVM 的线程堆栈信息 |
jvm | 查看当前 JVM 的信息 |
getstatic | 查看类的静态属性 |
ognl | 执行ognl命令 |
heapdump | dump java heap, 类似jmap命令的heap dump功能 |
jad | 反编译指定已加载类的源码 |
monitor | 方法执行监控 |
watch | 方法执行数据观测 |
trace | 方法内部调用路径,并输出方法路径上的每个节点上耗时 |
stack | 输出当前方法被调用的调用路径 |
tt | 方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测 |
quit/stop | 退出当前arthas客户端,stop表示关闭服务端,客户端均会退出 |
我们准备一个类做测试
public class TestDemo {
public static Long age = 30L;
public static Long get() {
return age;
}
public static Long add(int i) throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
System.out.println(get() - 1);
return age += i;
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < 100000000; i++) {
add(i);
}
Thread.currentThread().join();
}
}
监控面板dashboard
dashboard
命令
该命令监控的是整体信息,包含上(线程信息),中(内存信息),下(基础信息)
线程查看thread
参数名称 | 参数说明 |
---|---|
id | 线程id |
[n:] | 指定最忙的前N个线程并打印堆栈 |
[b] | 找出当前阻塞其他线程的线程 |
[i <value> ] | 指定cpu使用率统计的采样间隔,单位为毫秒,默认值为200 |
[–all] | 显示所有匹配的线程 |
采样最近5秒最忙的3个线程
thread -n 3 -i 5000
获得静态属性值getstatic
getstatic com.demo.TestDemo age
结果
field: age
@Integer[30]
Affect(row-cnt:1) cost in 27 ms.
反编译jad
jad com.demo.TestDemo
出参入参查看watch
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
method-pattern | 函数名表达式匹配 |
express | 观察表达式,默认值:{params, target, returnObj} |
condition-express | 条件表达式 |
[b] | 在函数调用之前观察 |
[e] | 在函数异常之后观察 |
[s] | 在函数返回之后观察 |
[f] | 在函数结束之后(正常返回和异常返回)观察 |
[E] | 开启正则表达式匹配,默认为通配符匹配 |
[x:] | 指定输出结果的属性遍历深度,默认为 1 |
watch com.demo.TestDemo add "{params}" -x 2
检查耗时代码trace
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
condition-express | 条件表达式 |
[E] | 开启正则表达式匹配,默认为通配符匹配 |
[n:] | 命令执行次数 |
#cost | 方法执行耗时 |
trace com.demo.TestDemo add
方法执行数据的时空隧道tt
参数名称 | 参数说明 |
---|---|
-t | 记录每次执行的情况 |
-n | 限制要记录的次数,防止内存溢出 |
⭐️万能的ognl
通过ognl
直接调用我们的代码
ognl '@com.demo.TestDemo@get()'
如果是调用spring bean中的方法只需要调用获取bean的静态方法即可
ognl '@com.demo.SpringContextHolder@getBean("mobileLoginSuccessHandler").defaultAuthorizationServerTokenServices'
更多请参考
🪣Docker如何使用Arthas
如果说你们公司使用的是docker部署的项目,该如何使用Arthas呢?
很简单,我们只需要将Arthas和我们的程序都打入进docker就好了
dockerfile
文件如下
FROM openjdk:8-jdk-alpine
COPY --from=hengyunabc/arthas:latest /opt/arthas /opt/arthas
MAINTAINER hellohesir@gmail.com
ENV TZ=Asia/Shanghai
RUN apk add --no-cache tini
RUN mkdir -p /app
WORKDIR /app
# 将编译好的应用也打入到容器中
ADD ./target/app.jar ./app.jar
ENTRYPOINT ["/sbin/tini", "--", "/bin/sh", "-c", "set -e && java ${JVM_PARAM} -Djava.security.egd=file:/dev/./urandom -jar app.jar && 1"]
问题
如果你使用了如skywalking
这一类基于jvm代理实现的软件时,会导致arthas无法正常工作,其原因是arthas是基于JavaAgent代理技术,当arthas尝试转换一个类时,它会触发SkyWalking代理再次增强该类。由于ByteBuddy重新生成了字节码,修改了字段和导入的类名,JVM对类字节码的验证失败,因此重新转换类将不成功。
skywalking的解决方案,新增如下配置中的其中一项,让skywalking先尝试从缓存中加载。
-Dskywalking.agent.class_cache_mode=MEMORY
-Dskywalking.agent.class_cache_mode=FILE
🚩吃井不忘挖水人
arthas官网
感恩开源~