这里是weihubeats,觉得文章不错可以关注公众号小奏技术,文章首发。拒绝营销号,拒绝标题党
背景
继之前我们研究了下skywalking
是什么以及skywalking
如何监控skywalking
我们并没有探讨过多的skywalking
原理
实际上skywalking
的实现原理就是java
的agent
所以我们再继续研究skywalking
之前还是先补补基础吧
什么是java 的agent
Java Agent 是 Java 1.5 版本之后引⼊的特性,其主要作⽤是在class
被加载之前对其拦截,已插⼊我们的监听字节码
- 官方文档: https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html#whatIs
用通俗易懂的话来说就是,我们可以不改变原有项目的任何代码,对项目进行一些增强操作
agent的加载时机
Java的Agent加载时机分别有两处
- 加载时刻增强(JVM 启动时加载)
- 动态增强(JVM 运行时加载)
我们要如何进行编码来实现我们的增强逻辑呢?
JVM提供了几个入口函数
加载时刻增强(JVM 启动时加载)
JVM 启动时加载启动时的入口方法提供了两个
// 方法一
public static void premain(String agentArgs, Instrumentation inst);
// 方法二
public static void premain(String agentArgs);
这两个函数的优先级是,如果没有方法一,则寻找方法二
动态增强(JVM 运行时加载)
JVM 运行时加载也提供了两个入口方法
// 方法一
public static void agentmain(String agentArgs, Instrumentation inst);
// 方法二
public static void agentmain(String agentArgs);
与上面JVM 启动时加载
的加载机制一样。如果没有方法一,则寻找方法二
如果刚接触agent
大家可能还是有点懵
没关系,下面我们先带大家写一个简单的demo来看看效果,这样就更容易理解
demo演示
如果不想编写我这里也提供了直接下载的源码
- 源码地址:https://github.com/weihubeats/weihubeats_demos/tree/master/java-demos/agent-demo
项目结构
核心demo
package com.weihubeats.agent.demo;
import java.lang.instrument.Instrumentation;
public class AgentDemo {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("========= 小奏技术1 premain init ========");
System.out.println("agentArgs:="+agentArgs);
}
public static void premain(String agentArgs) {
System.out.println("========= 小奏技术2 premain agentArgs init ========");
System.out.println("agentArgs:="+agentArgs);
}
}
注意我们写完代码之后需要创建一个resources/META-INF.MANIFEST.MF
文件
文件内容如下
Manifest-Version: 1.0
Can-Redefine-Classes: true # true表示能重定义此代理所需的类,默认值为 false(可选)
Can-Retransform-Classes: true # true 表示能重转换此代理所需的类,默认值为 false (可选)
Premain-Class: com.weihubeats.agent.demo.AgentDemo
这样我们的代码就编写完成了。这时候我们需要将整个项目打成一个jar,然后给其他项目接入
需要注意如果我们要测试agentmain
的使用,需要手动调用触发
import com.sun.tools.attach.VirtualMachine;
public class Main {
public static void main(String[] args) {
// 获取当前 Java 进程的 PID
String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
// 使用运行中的 JVM PID 创建一个 VirtualMachine 对象
VirtualMachine vm = VirtualMachine.attach(pid);
// 加载并附加 Java Agent 到运行中的 JVM
vm.loadAgent("path/to/your/agent.jar");
// 现在 agentmain 方法应该已经被调用了
}
}
从这里可以看到
agentmain
的增强方式触发有点麻烦,所以我们就暂>时不演示这种触发方式了
skywalking的实现也是基于premain
去实现的
打包
pom添加打包插件
<build>
<finalName>java-agent-demo</finalName>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>false</shadedArtifactAttached>
<createDependencyReducedPom>true</createDependencyReducedPom>
<createSourcesJar>true</createSourcesJar>
<shadeSourcesContent>true</shadeSourcesContent>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Premain-Class>com.weihubeats.agent.demo.AgentDemo</Premain-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</transformer>
</transformers>
<!--<artifactSet>
<excludes>
<exclude>*:gson</exclude>
<exclude>io.netty:*</exclude>
<exclude>io.opencensus:*</exclude>
<exclude>com.google.*:*</exclude>
<exclude>com.google.guava:guava</exclude>
<exclude>org.checkerframework:checker-compat-qual</exclude>
<exclude>org.codehaus.mojo:animal-sniffer-annotations</exclude>
<exclude>io.perfmark:*</exclude>
<exclude>org.slf4j:*</exclude>
</excludes>
<!– 将javassist包打包到Agent中 –>
<includes>
<include>javassist:javassist:jar:</include>
</includes>
</artifactSet>-->
<filters>
<filter>
<artifact>net.bytebuddy:byte-buddy</artifact>
<excludes>
<exclude>META-INF/versions/9/module-info.class</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
打包方式很简单,我们在项目根目录执行打包命令
mvn clean package
打完包后我们的项目target目录出现了我们的agent
spring boot 项目接入
我们在spring boot
项目添加如下启动参数
-javaagent:/Users/weihu/Desktop/sofe/java/weihubeats_demos/java-demos/agent-demo/target/java-agent-demo.jar=xiaozouParam=xiaozou
然后我们启动参数,查看log
可以看到启动成功了
总结
java agent的增强主要用两种方式agentmain
和premain
大多应用程序应该是使用的premain
,因为可以看到agentmain
还需要应用系统自己添加代码去触发
agent的一些基础方法和接入使用,其实agent的字节码编程开发才是最麻烦的。
后续我们也会学习字节码编程
参考
- https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html#whatIs
- https://github.com/apache/skywalking-java
- https://www.cnblogs.com/crazymakercircle/p/16635330.html#autoid-h2-6-0-0