概述
本文的主角是一款非常著名且使用的链路追踪工具:SkyWalking
SkyWalking是一个APM(application performance monitor)系统,专门为微服务、云原生和基于容器(Docker、Kubernetes、Mesos)的架构而设计,包含了云原生架构下的分布式系统的监控、跟踪、诊断功能。
通过agent的方式,可以做到对代码无侵入式的介入,实时实现整个服务链路的监控。本文的将要介绍的重点是SkyWalking如何通过agent实现如此全面的监控功能。这里提到的agent是在java中使用的agent技术,本文所讲的内容也是依托于java为基础的。
一、什么是agent?
1.1 静态agent
1.1.1 简介
JDK从1.5开始,引入了agent机制,用户可以通过-javaagent
参数使用agent技术。agent技术可以使JVM在加载class文件之前先加载agent文件,通过修改JVM传入的字节码来实现自定义代码的注入。
为什么称之为静态agent
?因为使用此种方式,不需要通过指定VM参数的方式,所以想要修改agent必须要对服务进行重启。
1.1.2 使用
下面我们动手实现一个简单的静态agent。
1.1.2.1 配置
实现静态agent需要配置agent的启动类,用来发现方法
premain
,这是实现静态agent的启动方法,因为是在jvm加载类之前,所以叫做pre-agent
静态agent通常有两种方式:
-
MANIFEST.MF 文件
需要在resources下创建META-INF文件夹,在内部创建MANIFEST.MF文件,其格式如下(注意最后一行要换行,否则idea或报错):
Manifest-Version: 1.0 Premain-Class: com.wjbgn.warriors.agent.StaticAgentTest Can-Redefine-Classes: true Can-Retransform-Classes: true
除此之外,还需要引入
maven-assembly-plugin
插件,否则MANIFEST.MF文件的内容会被maven打包后的内容覆盖掉。<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifestFile> src/main/resources/META-INF/MANIFEST.MF </manifestFile> </archive> </configuration> </plugin>
-
【推荐】引入编译插件 maven-assembly-plugin
直接使用
maven-assembly-plugin
插件就能达到使用agent的效果,所以推荐此方式。<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifestEntries> <Premain-Class>com.wjbgn.warriors.agent.StaticAgentTest</Premain-Class> <Can-Redefine-Classes>true</Can-Redefine-Classes> <Can-Retransform-Classes>true</Can-Retransform-Classes> </manifestEntries> </archive> </configuration> </plugin>
1.1.2.2 测试
-
创建一个测试类:
package com.wjbgn.warriors.agent; import java.lang.instrument.Instrumentation; /** * @description: 静态agent测试类 * @author:weirx * @date:2022/6/30 15:13 * @version:3.0 */ public class StaticAgentTest { /** * description: 静态agent启动类 * @param agentArgs * @param inst * @return: void * @author: weirx * @time: 2022/6/30 15:14 */ public static void premain(String agentArgs, Instrumentation inst) { // 在springboot启动前打印一下文字 System.out.println("this is static agent"); // 打印vm参数配置的agent参数 System.out.println(agentArgs); } }
-
使用assembly插件打包,在idea中:
当然也可以使用命令:`mvn assembly:single`。
打包后文件在项目的`target`下。
-
在idea添加启动参数:
蓝色部分是携带的参数,其余部分指定agent的jar包位置,完整命令如下:
```
-javaagent:E:\workspace\warriors\warriors-agent\target\warriors-agent-0.0.1-SNAPSHOT-jar-with-dependencies.jar=[testAgnet]
```
- 启动项目
如上所示,成功输出我们预期内容。
1.2 动态agent
1.2.1 简介
JDK在1.6版本开始,又引入了
attach
方式,对于运行中的应用程序,可以对其附加agent,这一操作让我们可以动态的修改已经加载的类。这也是称之为动态agent
的原因。
通过VirtualMachine的
attach(pid)
可以获得VirtualMachine实例,之后通过loadAgent(agent path)
方法将指定的agent加载到正在运行的JVM当中,实现动态加载。
1.2.2 使用
1.2.2.1 配置
想要使用VirtualMachine,需要引入对应的依赖:
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</