当前普遍使用容器化的部署方式,两种方式部署到tke容器中
curl -O https://alibaba.github.io/arthas/arthas-boot.jar
boot包仅有一百多 KB
执行
java -jar arthas.jar
即可进入arthas命令行,选择正在执行的java进程后,即可对进程进行相应的在线检测、诊断与调试。
我们知道,IDE中的Debug是通过JDPA技术实现了对代码的远程调试控制。而生产环境中是不会提供给我们JDPA
接口的,Arthas使用的是Java Agent技术。
Java Agent 又叫做 Java 探针,Java Agent 是在 JDK1.5 引入的,是一种可以动态修改 Java 字节码的技术。Java 类编译之后形成字节码被 JVM 执行,在 JVM 在执行这些字节码之前获取这些字节码信息,并且通过字节码转换器对这些字节码进行修改,来完成一些额外的功能,这种就是 Java Agent 技术。在许多文章中,Java Agent被描述为虚拟机级别的AOP。
实际上单说Java Agent的话,并不涉及字节码的修改,Java Agent是运行在main方法之前的拦截器,它内定的方法名叫 premain
,也就是说要先执行premain再执行main方法。也就是说,Java Agent仅仅是作为代理,为我们提供了在main函数运行之前的一个入口。
public static void premain(String agentArgs, Instrumentation inst);
public static void premain(String agentArgs);
Java Agent是一个概念,其具体的实现是借助于java.lang.instrument
包。在JDK1.5中,Instrument
要求在运行前利用命令行参数或者系统参数来设置代理类,在实际的运行之中,虚拟机在初始化之时(在绝大多数的 Java 类库被载入之前), instrumentation
的设置已经启动,并在虚拟机中设置了回调函数,检测特定类的加载情况,并完成实际工作。但是在实际的很多的情况下,我们没有办法在虚拟机启动之时就为其设定代理,这样实际上限制了 instrument
的应用。而 Java SE 6 的新特性改变了这种情况,通过 Java Tool API 中的 attach 方式,通过VirtualMachine.attach
和VirtualMachine.loadAgent
把agent attach到pid上,我们可以很方便地在运行过程中动态地设置加载代理类,以达到 instrumentation
的目的。而在运行时加载agent,就不再实现premain
,而是实现agentmain
这个方法。
public static void agentmain(String agentArgs, Instrumentation inst);
public static void agentmain(String agentArgs);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ED3jyFvy-1662182362358)(https://km.woa.com/files/photos/pictures/202108/1630248252_2_w1082_h508.jpg)]
整个流程如上图所示,总共可分为如下六步。
-javaagent
,传入 agent jar,Instrument Agent 被加载; eventHandlerVMinit
; eventHandlerVMinit
,启动了 Instrument Agent,用 sun.instrument.instrumentationImpl
类里的方法loadClassAndCallPremain
方法去初始化 Premain-Class
指定类的 premain
方法;eventHandlerVMinit
,注册了 class 解析的 ClassFileLoadHook
函数;JVMTI
的 ClassFileLoadHook
函数,钩子函数调用 sun.instrument.instrumentationImpl
类里的 transform
方法,通过TransformerManager
的 transformer
方法最终调用我们自定义的 Transformer
类的 transform
方法(在premain
或agentmain
中指定);对于我们通过attach的方式进行instrumentation
,则是对class文件进行重新加载,走5、6两个步骤。
在2.1节中介绍了JVMTI,JVMTI全称JVM Tool Interface,是JVM暴露出来的一些供用户扩展的接口集合,JVMTI是基于事件驱动的,也就是JVM每执行到一定的逻辑时就会调用一些事件的回调接口(如果回调接口存在),这些接口就可以被开发者扩展自己的逻辑。JVMTIAgent
是一个利用 JVMTI
暴露出来的接口提供了代理启动时加载(agent on load)、代理通过attach形式加载(agent on attach)和代理卸载(agent on unload)功能的动态库。而 instrument agent
可以理解为一类 JVMTIAgent
动态库,别名是 JPLISAgent(Java Programming Language Instrumentation Services Agent)
,也就是 专门为java语言编写的插桩服务提供支持的代理 。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wPNKFPhi-1662182362359)(https://km.woa.com/files/photos/pictures/202108/1630251937_41_w1166_h688.png)]
另一个扮演了重要角色的工具就是我们经常使用的ASM,Java字节码操纵框架。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。在许多Java项目中都能看到ASM的身影,比如CGLIB动态代理和lombok的@Data注解自动生成getter和setter。
在2.1节对字节码文件进行修改时,就是通过ASM直接修改class文件,以实现java程序的动态修改,如打印日志等。
help——查看命令帮助信息
cls——清空当前屏幕区域
reset——重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端关闭时会重置所有增强过的类
quit——退出当前 Arthas 客户端,其他 Arthas 客户端不受影响
stop——关闭 Arthas 服务端,所有 Arthas 客户端全部退出
dashboard——当前系统的实时数据面板,包含线程及内存信息
thread——查看当前 JVM 的线程堆栈信息
jvm——查看当前 JVM 的信息
ognl——执行ognl表达式
heapdump——dump java heap, 类似jmap命令的heap dump功能
sc——search class查看JVM已加载的类信息
sm——search method查看已加载类的方法信息
jad——反编译指定已加载类的源码
mc——内存编译器,内存编译.java文件为.class文件
retransform——加载外部的.class文件,retransform到JVM里
classloader——查看classloader的继承树,urls,类加载信息,使用classloader去getResource
此类命令,都通过字节码增强技术来实现的,会在指定类的方法中插入一些切面来实现数据统计和观测,线上使用时需谨慎,尽量明确需要观测的类、方法以及条件,诊断结束要执行stop或将增强过的类执行 reset 命令。
Alibaba Arthas实践–获取到Spring Context,然后为所欲为 · Issue #482 · alibaba/arthas (github.com)