小言_互联网的博客

Skywalking第二篇——Java Agent基础

264人阅读  评论(0)

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

Java Agent 入门

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

来吧,直接上Demo吧,一边看代码一边说(Maven项目,不解释了,不是Maven项目的话,可以自己百度一下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中运行,并被同一个类加载器所加载。

搞点有意思的

我知道,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~


转载:https://blog.csdn.net/xxxlxy2008/article/details/102480865
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场