听说你没法在JRE中使用arthas?不,你可以!

简介: 听说你没法在JRE中使用arthas?不,你可以!

本文是《容器中的Java》系列文章之 5/n ,欢迎关注后续连载 :) 。


之前经常遇到的问题是,排查问题需要挂arthas,但客户用的是JRE,没法挂载arthas。就只能让客户更换成JDK,再重新部署、排查问题。

很多有用的现场,在这个过程中也会丢失,最终导致问题排查效率降低。于是就探索了下如何在JRE环境中,使用artahs。

复现问题

如果一个Bug 没法复现,研发大概率是无法修复的。 —— by 网友

我们写一个Java例子和Dockerfile:

// ./src/main/java/Main.java
public class Main {
  public static void main(String[] args) throws Exception {
    while (true) {
      System.out.println("hello!");
      Thread.sleep(30 * 1000);
    }
  }
}
# ./Dockerfile
FROM openjdk:8-jdk-alpine as builder
COPY ./ /app
WORKDIR /app/src/main/java/
# 编译java文件
RUN javac Main.java
# 运行时容器使用JRE
FROM openjdk:8-jre-alpine
RUN apk add bash curl busybox-extras
WORKDIR /app/src/main/java/
# 将arthas copy 到容器中
COPY --from=hengyunabc/arthas:latest /opt/arthas /opt/arthas
COPY --from=builder /app/src/main/java/ /app/src/main/java/
CMD ["java", "Main"]

构建并正常启动应用,并尝试用arthas attach,此处为了便于了解原理,我们使用as.sh来执行:

$ # 构建镜像
$ docker build . -t example-attach
$ # 启动容器
$ docker run --name example-attach --rm example-attach
$ # 在另一个终端进入容器,执行as.sh
$ docker exec -it example-attach sh
/app/src/main/java $ /opt/arthas/as.sh
Arthas script version: 3.6.7
tools.jar was not found, so arthas could not be launched!

行吧,咱们先用jdk运行下,先看下arthas是怎么attach起来的:

# 替换容器为JDK镜像并运行
# 先启动Attach Listener
$ pid=1 ;\
  touch /proc/${pid}/cwd/.attach_pid${pid} && \
  kill -SIGQUIT ${pid} && \
  sleep 2 &&
  ls /proc/${pid}/root/tmp/.java_pid${pid}
# -x表示调试执行,会输出执行了哪些命令;1为java进程pid
$ bash -x /opt/arthas/as.sh 1
...
+ /usr/lib/jvm/java-1.8-openjdk/bin/java -Xbootclasspath/a:/usr/lib/jvm/java-1.8-openjdk/lib/tools.jar -Djava.awt.headless=true -jar /opt/arthas/arthas-core.jar -pid 1 -core /opt/arthas/arthas-core.jar -agent /opt/arthas/arthas-agent.jar
...
+ telnet 127.0.0.1 3658
...

可以看到,最主要的逻辑是java -jar arthas-core.jar -pid 1 -core arthas-core.jar -agent arthas-agent.jar,然后再去连接3658端口。

-Xbootclasspath/a:tools.jar当然有用,但是在JRE中没有tools.jar,所以可以忽略。

那么上面的逻辑我们直接尝试在JRE上运行呢?我们继续在JRE镜像中执行上面的命令:

# 替换容器为JRE镜像并运行
# 先启动Attach Listener
$ pid=1 ;\
  touch /proc/${pid}/cwd/.attach_pid${pid} && \
  kill -SIGQUIT ${pid} && \
  sleep 2 &&
  ls /proc/${pid}/root/tmp/.java_pid${pid}
$ cd /opt/arthas/
$ java -jar arthas-core.jar -pid 1 -core arthas-core.jar -agent arthas-agent.jar
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/tools/attach/AgentLoadException
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
    at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
    at java.lang.Class.getMethod0(Class.java:3018)
    at java.lang.Class.getMethod(Class.java:1784)
    at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: com.sun.tools.attach.AgentLoadException
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 7 more

对照代码来看,这个报错其实很正常,arthas-core中会调用Attach API,然后加载Agent(重点代码都已经标记):

熟悉类加载机制的同学们可能猜到了,Arthas.class中依赖了com.sun.tools.的一些类,所以上面的报错其实是在类链接的时候就报错了。这也是为什么报错的stacktrace中没有任何arthas的包出现。

看着上面arthas的代码,就不得不思考下如何规避掉对tools.jar的依赖了。

如何去除对JDK的依赖

第一,像图中这样,直接调用com.sun.tools.attach.*相关类、方法,是肯定不行的,上面的报错其实已经很说明情况了。另外,通过反射也不行,tools.jar就不存在,自然无法加载这些类。

第二,能不能通过我们手动把tools.jar放到容器中的方式呢?理论上确实可以,相关issue也说了具体的操作和注意事项:

理论上这样确实能工作,但其一,tools.jar是根据不同的jdk发行版、不同的jdk版本而不同的。比如,同样在eclipse-temurin:11-jre-alpine里面也挂不上arthas,你就不能copy jdk8的tools.jar来处理。

我们在继续看下有没有其他方式来挂agent。

第三,看了一圈,ByteBuddy实现了attach agent的功能。但ByteBuddy是通过逐个尝试的方式来尝试attach,而且几乎都依赖tools.jar,大家感兴趣的话,可以看下下面几个策略的实现:

看起来我们可以自己实现一个AttachmentProvider,然后改造arthas通过ByteBuddy挂agent就可以了。

刚开始也是这样想的,甚至代码都写了一半了。直到晚上回家路上,想到上一篇文章中说的,可以通过自定义脚本或者jattach的方式来attach。

第四,通过jattach来加载。

参考jattach的文档,如下操作下即可:

# 安装 jattach
$ apk add jattach
# 挂载arthas-agent.jar
$ jattach 1 load instrument false /opt/arthas/arthas-agent.jar
Connected to remote JVM
JVM response code = 0
return code: 0
# netstat确认下监听端口
$ netstat -alnp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:3658          0.0.0.0:*               LISTEN      1/java
...
# 连接对应端口
$ java -jar /opt/arthas/arthas-client.jar 127.0.0.1 3658

经过了如上操作,arthas就可以畅快执行了:

最终解决方案

咱知道有的时候,我们仅仅需要一个答案:

$ pid=1 ;\
  jattach ${pid} load instrument false /opt/arthas/arthas-agent.jar && \
  java -jar /opt/arthas/arthas-client.jar 127.0.0.1 3658

总结

相比上一次musl+jdk8+pid 1的问题,这次我们用attach机制做了更多的事情。开发同学遇到JRE,再也不用换JDK、换镜像,能够最大程度的保留现场,问题排查就变得顺畅高效的多了。

当然,在容器环境中,Java应用遇到的奇奇怪怪的情况,不止如此,欲知后事如何,且听《容器中的Java》系列下回分解吧。

相关文章
|
Arthas 缓存 Java
在 Windows 下的 Arthas 快速安装 | 学习笔记
快速学习在 Windows 下的 Arthas 快速安装
在 Windows 下的 Arthas 快速安装 | 学习笔记
|
4月前
|
机器学习/深度学习 人工智能 API
用Macbook微调Qwen3!手把手教你用微调给Qwen起一个新名字
本文介绍如何在MacBook上使用苹果MLX框架高效微调Qwen3大模型。借助MLX的高性能计算与统一内存架构,仅需2分钟即可完成训练,内存占用低至2GB,推理速度达400 Token/s,并支持快速部署为本地API服务,展现Mac轻薄本的强大AI生产力潜力。
1597 16
用Macbook微调Qwen3!手把手教你用微调给Qwen起一个新名字
|
5月前
|
Arthas 运维 监控
一次线上CPU飙高排查实录:从Arthas到JVM调优的深入之旅
本文记录了一次线上Java应用CPU使用率异常升高的故障排查过程。通过使用阿里巴巴开源工具Arthas,快速定位到问题根源:日志切面中存在性能缺陷的正则表达式在处理超长字符串时引发“回溯爆炸”,导致CPU资源耗尽。文中详细介绍了排查步骤、问题分析及解决方案,包括利用Arthas进行实时监控、线程分析、方法监控和在线热更新修复。最后总结了排查经验与技术启示,强调工具掌握、性能意识与防御式编程的重要性。
878 0
|
10月前
|
消息中间件 NoSQL 大数据
RocketMQ实战—5.消息重复+乱序+延迟的处理
本文围绕RocketMQ的使用与优化展开,分析了优惠券重复发放的原因及解决方案。首先,通过案例说明了优惠券系统因消息重复、数据库宕机或消费失败等原因导致重复发券的问题,并提出引入幂等性机制(如业务判断法、Redis状态判断法)来保证数据唯一性。其次,探讨了死信队列在处理消费失败时的作用,以及如何通过重试和死信队列解决消息处理异常。接着,分析了订单库同步中消息乱序的原因,提出了基于顺序消息机制的代码实现方案,确保消息按序处理。此外,介绍了利用Tag和属性过滤数据提升效率的方法,以及延迟消息机制优化定时退款扫描的功能。最后,总结了RocketMQ生产实践中的经验.
RocketMQ实战—5.消息重复+乱序+延迟的处理
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细介绍了Spring框架中的核心概念——Spring Bean的生命周期,包括实例化、属性赋值、接口回调、初始化、使用及销毁等10个阶段,并深入剖析了相关源码,如`BeanFactory`、`DefaultListableBeanFactory`和`BeanPostProcessor`等关键类与接口。通过理解这些核心组件,读者可以更好地掌握Spring Bean的管理和控制机制。
1671 1
|
Arthas Java 测试技术
听说你没法在 JRE 中使用 arthas?不,你可以
本文是《容器中的 Java》系列文章之 5/n ,欢迎关注后续连载 :) 。
听说你没法在 JRE 中使用 arthas?不,你可以
|
算法 Oracle Java
一文详解|从JDK8飞升到JDK17,再到未来的JDK21
本文深入浅出地解析了从JDK8到JDK17版本升级的新特性,并展望后续将会更新的JDK21.
12142 8
|
数据采集 存储 运维
如何使用SkyWalking收集分析分布式系统的追踪数据
通过以上步骤,你可以使用 SkyWalking 工具实现对分布式系统的数据采集和可视化。SkyWalking 提供了强大的追踪和度量功能,帮助开发者和运维人员更好地理解系统的性能状况。欢迎关注威哥爱编程,一起学习成长。
748 0
|
存储 监控 安全
深度剖析Linux进程的内部机制:一探/proc/pid的奥秘
深度剖析Linux进程的内部机制:一探/proc/pid的奥秘
3160 0