首页 > 其他分享 >听说你没法在 JRE 中使用 arthas?不,你可以

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

时间:2023-03-02 18:32:15浏览次数:38  
标签:JRE java pid jar attach 没法 arthas tools

作者:卜比


本文是《容器中的 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(重点代码都已经标记):​


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


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


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


如何去除对 JDK 的依赖


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


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


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


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


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


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


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


看起来我们可以自己实现一个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就可以畅快执行了:


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

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


最终解决方案


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


$ 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》系列下回分解吧。

标签:JRE,java,pid,jar,attach,没法,arthas,tools
From: https://blog.51cto.com/u_13778063/6096555

相关文章

  • 听说你没法在 JRE 中使用 arthas?不,你可以
    作者:卜比本文是《容器中的Java》系列文章之5/n,欢迎关注后续连载......
  • Java的特性和优点,三大版本,JDK,JRE,JVM
    Java的特性和优点面对对象跨平台性简单高性能分布式动态性多线程安全的健壮的Java的三大版本JavaSE:标准版(桌面程序,控制台开发...)JavaME:......
  • Java面试宝典_君哥讲解笔记02 描述JDK、JRE、JVM之间的关系、如果main方法声明成priva
    系列文章目录文章目录​​系列文章目录​​​​java基础面试题​​​​请描述JDK、JRE、JVM之间的关系​​​​如果main方法被声明成private会怎么样呢?​​​​&和&&区别在......
  • sqlite没法查出数据
    能够正常插入数据,再次运行插入数据的代码,报错主键重复,说明数据库数据是存在的。只是插入后再查出就没有数据,显示得是undefined。 检查一下语法,没问题啊,到底啥问题(图中......
  • JDK、JRE、JVM
    JDK:JavaDevelopmentKit(java开发者工具)JRE:JavaRuntimeEnvironment(java运行时环境)JVM:JAVA VirtualMachine(java虚拟机) ......
  • Arthas进阶
    《Arthas进阶》学习目标类和类加载器相关的命令monitor/watch/trace/stack等核心命令的使用火焰图的生成Arthas实战案例dump作用将已加载类的字节码文件保存到特......
  • Arthas基础学习
    《Arthas基础学习》概述Arthas(阿尔萨斯)能为你做什么?Arthas是Alibaba开源的Java诊断工具,深受开发者喜爱。当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:......
  • 学习笔记分享:java面试(JDK、JRE、JVM的区别)
    简答题、问答题:1.JDK、JRE、JVM的区别:1)JDK:java开发工具包,是java的核心,包括:JRE+编译、运行等命令工具2)JRE:java运行环境,是运行java程序所必须的环境集合,包括:JVM+......
  • Arthas,热更新线上代码神器
    大家好,我是小悟前言日常开发过程中,可能会遇到小问题,本地代码改完后,总得重新打包部署,线上代码才会生效,这样太麻烦了,这个时候就可以考虑使用Arthas(阿尔萨斯)工具来热更新线上代......
  • arthas内存也可以分析 -线程死锁 -cpu高
    https://blog.csdn.net/qq_43692950/article/details/122688520......