转载

Skywalking第二篇——Java Agent基础

发车篇提到,要用 Skywalking 监控一个应用,需要在其 VM 参数中添加 “-javaagent:skywalking-agent.jar”(省略skywalking-agent.jar的完整路径),这其实用了Java探针技术,算是个比较老的技术了,本节就简单介绍一下Java Agent。

Skywalking第二篇——Java Agent基础

Java Agent 入门

Skywalking第二篇——Java Agent基础

Java Agent是从 JDK1.5 开始引入的,用一句概括其功能的话就是“在main()函数之前的一个拦截器”,也就是在执行main函数前,先执行Agent中的代码。

来吧,直接上Demo吧,一边看代码一边说( Maven 项目,不解释了 ,不是Maven项目的话,可以自己百度一下Java Agent怎么玩,也挺简单的):

Skywalking第二篇——Java Agent基础

先看TestAgent的代码:

public class TestAgent {

public static void premain(String agentArgs,

Instrumentation inst) {

System.out.println("this is a java agent.");

System.out.println("参数:" + agentArgs + "/n");

}

}

Java Agent的入口是premain()方法,听名字就知道是在main()方法前面执行的,有两个重载,如下所示:

public static void premain(String agentArgs,

Instrumentation inst); 【1

public static void premain(String agentArgs); 【2

如果两个同时存在的时候,【2】将会被忽略,只执行【1】

pom.xml里面我们需要用到一个插件:

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-jar-plugin</artifactId>

<version>2.6</version>

<configuration>

<archive>

<manifestEntries>

<premain-class>com.xxx.TestAgent</premain-class>

</manifestEntries>

</archive>

</configuration>

</plugin>

这个插件是在打包的时候,写一下MANIFEST.MF,重点是写 premain-class :

Manifest-Version: 1.0

premain-class: com.xxx.TestAgent # 这里这里

Archiver-Version: Plexus Archiver

Built-By: xxx

Created-By: Apache Maven 3.6.0

Build-Jdk: 1.8.0_191


最后来看Main这个类:

public class Main {

public static void main(String[] args) {

System.out.println("TestAgent Main!");

}

}

接下来maven package一下,得到test-agent.jar这个包,然后设置VM options:

-javaagent:/Users/xxx/ks/TestAgent/target/test-agent.jar=TestAgentArgs

这里等号之后,就是传入premain()方法的参数哈

执行main()方法,会得到如下输出:

this is a java agent.

参数:TestAgentArgs

TestAgent Main!

注意:Java Agent的代码与你的main方法在同一个JVM中运行,并被同一个类加载器所加载。

Skywalking第二篇——Java Agent基础

搞点有意思的

Skywalking第二篇——Java Agent基础

我知道,Hello World肯定满足不了你们,下面用 Java Agent + Byte Buddy 实现一个统计方法执行时间的功能。

Byte Buddy 的 API 在后面会单独来一篇介绍一下,这里不深入追究,简单说明每个 API 的作用即可。

整个项目的结构不变,首先多加两个Byte Buddy的依赖:

<dependency>

<groupId>net.bytebuddy</groupId>

<artifactId>byte-buddy</artifactId>

<version>1.9.2</version>

</dependency>

<dependency>

<groupId>net.bytebuddy</groupId>

<artifactId>byte-buddy-agent</artifactId>

<version>1.9.2</version>

</dependency>

然后来看 TestAgent的代码

public class TestAgent {

public static void premain(String agentArgs, Instrumentation inst) {

AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {

public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder,

TypeDescription typeDescription,

ClassLoader classLoader,

JavaModule module) {

return builder

// 拦截任意方法

.method(ElementMatchers.<MethodDescription>any())

// 拦截到的方法委托给TimeInterceptor

.intercept(MethodDelegation.to(TimeInterceptor.class));

}

};

new AgentBuilder // Byte Buddy专门有个AgentBuilder来处理Java Agent的场景

.Default()

// 根据包名前缀拦截类

.type(ElementMatchers.nameStartsWith("com.xxx"))

// 拦截到的类由transformer处理

.transform(transformer)

.installOn(inst);

}

}

当 Agent 拦截到符合条件的类时,会交给我们的  AgentBuilder.Transformer 实现处理,当 Transformer 拦截到符合条件的方法时,会交给我们的 TimeInterceptor 处理。 TimeInterceptor 的具体实现如下:

public class TimeInterceptor {

@RuntimeType

public static Object intercept(@Origin Method method,

@SuperCall Callable<?> callable)

throws Exception {

long start = System.currentTimeMillis();

try {

return callable.call(); // 执行原函数

} finally {

System.out.println(method.getName() + ":"

+ (System.currentTimeMillis() - start)

+ "ms");

}

}

}

TimeInterceptor 就类似于 AOP 的环绕切面。这里通过 @SuperCall 注解注入的 Callable 实例可以调到被拦截的目标方法(即使目标方法带参数,这里也不用传哈);这里通过 @Origin 注入的 Method 就是目标方法的元信息,没啥可说的。

在Skywalking中用到的 Byte Buddy 知识在下一篇文章中会进行说明的,容我整理整理

Main.java 中sleep 10s, VM options与前面的示例相同,不再赘述。执行 main() 方法,得到输出如下:

TestAgent Main!

main:10001ms

好了,Java Agent的基础知识和练手功能,就这样吧,Hello World 总是简单的,

╮(╯_╰)╭ ~ see you~

Skywalking第二篇——Java Agent基础

原文  http://mp.weixin.qq.com/s?__biz=MzU5Mjc5OTY5Ng==&mid=2247484438&idx=1&sn=b2518c3032e2a81ee431bee940ea5e22
正文到此结束
Loading...