分布式链路追踪_skywalking_学习(4)
一、分布式链路追踪_skywalking :java-agent 环境搭建
1、Java agent 是什么?
Java agent 是 java 命令的一个参数。参数 javaagent 可以用于指定一个 jar 包。
- 1)这个 jar 包的 MANIFEST.MF 文件必须指定 Premain-Class 项。
- 2)Premain-Class 指定的那个类必须实现 premain() 方法。
当 Java 虚拟机启动时,在执行 main 函数之前,JVM 会先运行 - javaagent 所指定 jar 包内 Premain-
Class 这个类的 premain 方法 。
2、如何使用 java agent?
使用 java agent 需要几个步骤:
- 1)定义一个 MANIFEST.MF 文件,必须包含 Premain-Class 选项,通常也会加入Can-Redefine-
Classes 和 Can-Retransform-Classes 选项。 - 2)创建一个Premain-Class 指定的类,类中包含 premain 方法,方法逻辑由用户自己确定。
- 3)将 premain 的类和 MANIFEST.MF 文件打成 jar 包。
- 4)使用参数 -javaagent: jar 包路径 启动要代理的方法。
3、创建工程 java_agent_demo
3.1 打开 idea,创建 java_agent_demo 的 maven 工程。
--> idea --> File
--> New --> Project
--> Maven
Project SDK: ( 1.8(java version "1.8.0_131" )
--> Next
--> Groupld : ( djh.it )
Artifactld : ( java_agent_demo )
Version : 1.0-SNAPSHOT
--> Name: ( java_agent_demo )
Location: ( \java_agent_demo\ )
--> Finish
3.2 在工程 java_agent_demo (模块)中的 pom.xml 中导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>djh.it</groupId>
<artifactId>java-agent-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<!--自动添加META-INF/MANIFEST.MF -->
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<Premain-Class>PreMainAgent</Premain-Class>
<Agent-Class>PreMainAgent</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
3.3 在工程 java_agent_demo (模块)中,创建 类 PreMainAgent.java
/**
* 2024-5-21 创建 类 PreMainAgent.java
*/
import java.lang.instrument.Instrumentation;
public class PreMainAgent {
//先执行双参方法,后执行单参方法
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("=========premain方法执行1========双参数");
System.out.println(agentArgs);
}
/**
* 如果不存在 premain(String agentArgs, Instrumentation inst)
* 则会执行 premain(String agentArgs)
* @param agentArgs
*/
public static void premain(String agentArgs) {
System.out.println("=========premain方法执行2========单参数");
System.out.println(agentArgs);
}
}
3.4 把 工程 java_agent_demo 打包 java_agent_demo.jar
--> idea
--> 右边 Maven
--> Lifecycle
--> package
--> 打包后的文件存在于:java_agent_demo/target/mavent-status/java_agent_demo_1.0_SNAPSHOT.jar
4、创建测试工程 java_agent_user 的 maven 工程。
4.1 打开 idea,创建 java_agent_user 的 maven 工程。
--> idea --> File
--> New --> Project
--> Maven
Project SDK: ( 1.8(java version "1.8.0_131" )
--> Next
--> Groupld : ( djh.it )
Artifactld : ( java_agent_user )
Version : 1.0-SNAPSHOT
--> Name: ( java_agent_user )
Location: ( \java_agent_user\ )
--> Finish
4.2 java_agent_user 工程的 pom.xml 文件。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itcast</groupId>
<artifactId>java_agent_user</artifactId>
<version>1.0-SNAPSHOT</version>
</project>
4.3 在 java_agent_user 工程中,创建 测试类 Main.java
/**
* 2024-5-21 创建 测试类 Main.java
*/
package djh.it.agent;
public class Main {
public static void main(String[] args) {
System.out.println("main 方法执行");
}
}
5、在 java_agent_user 工程中,添加参数,运行 mani() 方法,查看测试结果。
--> idea
--> Edit Configuration...
--> Run/Debug Configurations
--> Application/Main
--> Configuration
--> VM options : ( -javaagent:D:\java_test\Intesij_idea\java_agent_demo\target\java-agent-demo-1.0-SNAPSHOT.jar=test )
--> OK.
6、可以看到,先运行 java_agent_demo-1.0-SNAPSHOT.jar 里的 primain() 方法,再运行 main() 方法。
二、分布式链路追踪_skywalking :javaagent 原理-统计方法调用时间
1、Skywalking 中对每个调用的时长都进行了统计,可以使用 ByteBuddy 和 Java agent 技术来统计方法的调用时长。
-
Byte Buddy 是开源的、基于 Apache 2.0 许可证的库,它致力于解决字节码操作和 instrumentation API 的复杂性。
-
Byte Buddy 所声称的目标是将显式的字节码操作隐藏在一个类型安全的领域特定语言背后。通过使用 Byte Buddy,任何熟悉 Java 编程语言的人都有望非常容易地进行字节码操作。
-
Byte Buddy 提供了额外的 API 来生成 Java agent,可以轻松的增强我们已有的代码。
2、在工程 java_agent_demo (模块)中,修改 类 PreMainAgent.java
/**
* 2024-5-21 创建 类 PreMainAgent.java
*/
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;
import java.lang.instrument.Instrumentation;
public class PreMainAgent {
// /**
// * 在这个 premain 函数中,开发者可以进行对类的各种操作。
// * 1、agentArgs 是 premain 函数得到的程序参数,随同 “– javaagent”一起传入。与 main 函数不同的是,
// * 这个参数是一个字符串而不是一个字符串数组,如果程序参数有多个,程序将自行解析这个字符串。
// * 2、Inst 是一个 java.lang.instrument.Instrumentation 的实例,由 JVM 自动传入。*
// * java.lang.instrument.Instrumentation 是 instrument 包中定义的一个接口,也是这个包的核心部分,
// * 集中了其中几乎所有的功能方法,例如类定义的转换和操作等等。
// * @param agentArgs
// * @param inst
// */
// public static void premain(String agentArgs, Instrumentation inst) {
// System.out.println("=========premain方法执行1========");
// System.out.println(agentArgs);
// }
//
// /**
// * 如果不存在 premain(String agentArgs, Instrumentation inst)
// * 则会执行 premain(String agentArgs)
// * @param agentArgs
// */
// public static void premain(String agentArgs) {
// System.out.println("=========premain方法执行2========");
// System.out.println(agentArgs);
// }
public static void premain(String agentArgs, Instrumentation inst) {
//创建一个转换器,转换器可以修改类的实现
//ByteBuddy对java agent提供了转换器的实现,直接使用即可
AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule) {
return builder
// 拦截任意方法
.method(ElementMatchers.<MethodDescription>any())
// 拦截到的方法委托给TimeInterceptor
.intercept(MethodDelegation.to(MyInterceptor.class));
}
};
new AgentBuilder // Byte Buddy专门有个AgentBuilder来处理Java Agent的场景
.Default()
// 根据包名前缀拦截类
.type(ElementMatchers.nameStartsWith("djh.it.agent"))
// 拦截到的类由transformer处理
.transform(transformer)
.installOn(inst);
}
}
3 在工程 java_agent_demo (模块)中,创建 类 MyInterceptor.java
/**
* 2024-5-21 创建 类 MyInterceptor.java
*/
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
public class MyInterceptor {
@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");
}
}
}
4、在 java_agent_user 工程中,修改 测试类 Main.java
/**
* 2024-5-21 创建 测试类 Main.java
*/
package djh.it.agent;
public class Main {
public static void main(String[] args) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello World");
}
}
5、可以看到,先运行 java_agent_demo-1.0-SNAPSHOT.jar 里的 primain() 方法,再运行 main() 方法。并且对 djh.it.agent 包下的方法,进行拦截统计方法执行时间。
三、分布式链路追踪_skywalking :OpenTracing 介绍
1、Open Tracing 介绍
OpenTracing 通过提供平台无关、厂商无关的 API,使得开发人员能够方便的添加(或更换)追踪系统的实现。OpenTracing 中最核心的概念就是
Trace。
2、Trace 的概念
在广义上,一个 trace 代表了一个事务或者流程在(分布式)系统中的执行过程。在 OpenTracing 标准
中,trace 是多个 span 组成的一个有向无环图(DAG),每一个 span 代表 trace 中被命名并计时的连续性的执行片段。
3、Span 的概念
-
一个 Span 代表系统中具有开始时间和执行时长的逻辑运行单元。span 之间通过嵌套或者顺序排列建立逻辑因果关系。
-
Span 里面的信息包括:操作的名字,开始时间和结束时间,可以附带多个 key:value 构成的 Tags(key必须是 String,value 可以是 String, bool 或者数字),还可以附带 Logs 信息(不一定所有的实现都支持)也是 key:value形式。
-
一个 span 可以和一个或者多个 span 间存在因果关系。OpenTracing 定义了两种关系: ChildOf 和 FollowsFrom 。这两种引用类型代表了子节点和父节点间的直接因果关系。未来,OpenTracing 将支持非因果关系的 span 引用关系。(例如:多个 span 被批量处理,span 在同一个队列中,等等)hildOf 很好理解,就是父亲 Span 依赖另一个孩子 Span。比如函数调用,被调者是调用者的孩子,比如说 RPC 调用,服务端那边的Span,就是 ChildOf 客户端的。很多并发的调用,然后将结果聚合起来的操作,就构成了 ChildOf 关系。
-
如果父亲 Span 并不依赖于孩子 Span 的返回结果,这时可以说它他构成 FollowsFrom 关系。
4、 Log 的概念
每个 span 可以进行多次 Logs 操作,每一次 Logs 操作,都需要一个带时间戳的时间名称,以及可选的任意大小的存储结构。
5、Tags 的概念
每个 span 可以有多个键值对(key:value)形式的 Tags,Tags 是没有时间戳的,支持简单的对 span 进行注解和补充。
上一节关联链接请点击:
# 分布式链路追踪_skywalking_学习(3)