java agent介绍
java agent本质上可以理解为一个插件,该插件就是一个精心提供的jar包,这个jar包通过JVMTI(JVM ToolInterface)完成加载,最终借助JPLISAgent(JavaProgramming Language Instrumentation Services Agent)完成对目标代码的修改。
java agent技术的主要功能如下:
可以在加载java文件之前做拦截把字节码做修改
可以在运行期将已经加载的类的字节码做变更
还有其他的一些小众的功能
获取所有已经被加载过的类
获取所有已经被初始化过了的类
获取某个对象的大小
将某个jar加入到bootstrapclasspath里作为高优先级被bootstrapClassloader加载
将某个jar加入到classpath里供AppClassloard去加载
设置某些native方法的前缀,主要在查找native方法的时候做规则匹配
java Instrumentation API
实现agent启动方法
Java Agent支持目标JVM启动时加载,也支持在目标JVM运行时加载,这两种不同的加载模式会使用不同的入口函数,如果需要在目标JVM启动的同时加载Agent,那么可以选择实现下面的方法:
public static void premain(String agentArgs, Instrumentation inst);
public static void premain(String agentArgs);
如果希望在目标JVM运行时加载Agent,则需要实现下面的方法:
public static void agentmain(String agentArgs, Instrumentation inst);
public static void agentmain(String agentArgs);
这两组方法的第一个参数AgentArgs是随同“–javaagent”一起传入的程序参数,如果这个字符串代表了多个参数,就需要自己解析这些参数。inst是Instrumentation类型的对象,是JVM自动传入的,我们可以拿这个参数进行类增强等操作。
MANIFEST.MF 作用
If you remove META-INF from a jar then there is no MANIFEST.MFand so java -jar can't find the main class.
Agent需要打包成一个jar包,在ManiFest属性中指定“Premain-Class”或者“Agent-Class”,且需根据需求定义Can-Redefine-Classes和Can-Retransform-Classes:
Manifest-Version: 1.0
Implementation-Title: apm-agent
Implementation-Version: 6.6.0
Built-By: wusheng
Specification-Vendor: The Apache Software Foundation
Can-Redefine-Classes: true
Specification-Title: apm-agent
Implementation-Vendor-Id: org.apache.skywalking
Implementation-Vendor: The Apache Software Foundation
Premain-Class: org.apache.skywalking.apm.agent.SkyWalkingAgent
Can-Retransform-Classes: true
Created-By: Apache Maven 3.6.1
Build-Jdk: 1.8.0_232
Specification-Version: 6.6
Implementation-URL: http://maven.apache.org
启动时修改
启动时修改主要是在jvm启动时,执行native函数的Agent_OnLoad方法,在方法执行时,执行如下步骤:
创建InstrumentationImpl对象
监听ClassFileLoadHook事件
调用InstrumentationImpl的loadClassAndCallPremain方法,在这个方法里会去调用javaagent里MANIFEST.MF里指定的Premain-Class类的premain方法
运行时修改
运行时修改主要是通过jvm的attach机制来请求目标jvm加载对应的agent,执行native函数的Agent_OnAttach方法,在方法执行时,执行如下步骤:
创建InstrumentationImpl对象
监听ClassFileLoadHook事件
调用InstrumentationImpl的loadClassAndCallAgentmain方法,在这个方法里会去调用javaagent里MANIFEST.MF里指定的Agentmain-Class类的agentmain方法
agent通过 org.apache.skywalking.apm.agent.core.boot.BootService 实现了整体的插件化,agent启动会加载所有的BootService实现,并通过 ServiceManager 来管理这些插件的生命周期,采集jvm指标、gRPC连接管理、调用链数据维护、数据上报OAP这些服务均是通过这种方式扩展。
然后,agent还通过bytebuddy以javaagent的模式,通过字节码增强的机制来构造AOP环境,再提供PluginDefine的规范方便探针的开发,最终实现非侵入性的数据埋点,采集调用链数据。
参考链接:
Instrumentation 新功能
https://www.ibm.com/developerworks/cn/java/j-lo-jse61/index.html
JVM 源码分析之javaagent 原理完全解读
https://www.infoq.cn/article/javaagent-illustrated/
https://xie.infoq.cn/article/a6012a49401802c963d416970
通过使用 Byte Buddy,便捷地创建 Java Agent
https://www.infoq.cn/article/Easily-Create-Java-Agents-with-ByteBuddy
最全面!一文让你看懂无侵入的微服务探针原理
https://www.infoq.cn/article/q1ivDiHCAFwKYF8Zmyj5
从 0-1 开发 Java 性能剖析工具
https://www.infoq.cn/article/esVkcjQPTh3YzDFUCUKd
虚拟机技术
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html
vivo 调用链Agent 原理及实践
https://xie.infoq.cn/article/c98b32ef8bf0193e4bc40be70
ASM
Byte Buddy 教程
https://notes.diguage.com/byte-buddy-tutorial/
JVMTI(JVMTool Interface)
https://docs.oracle.com/en/java/javase/16/docs/specs/jvmti.html
流概念
https://ci.apache.org/projects/flink/flink-docs-release-1.11/concepts/index.html