4. Skywalking principle

Skywalking principle

 4.1 Principle of java agent

As we know above, to use Skywalking to monitor services, you need to add "-javaagent:/usr/local/skywalking/apache-skywalking-apm-bin/agent/skywalking-agent.jar" to its VM parameters.


Java agent technology is used here .
What is a Java agent?
Java agent is a parameter of the java command. The parameter javaagent can be used to specify a jar package.

1. The MANIFEST.MF file of this jar package must specify the Premain-Class item.
2. The class specified by Premain-Class must implement the premain() method.
   When the Java virtual machine is started, before executing the main function, the JVM will first run
   the premain method of the Premain- Class class in the jar package specified by -javaagent .
   How to use java agent?
   Using java agent requires several steps:
3. Define a MANIFEST.MF file, which must include the Premain-Class option, and usually also add the Can-Redefine-
   Classes and Can-Retransform-Classes options.
4. Create a class specified by Premain-Class, the class contains premain methods, and the method logic is determined by the user.
5. Type the premain class and the MANIFEST.MF file into a jar package.
6. Use the parameter -javaagent: jar package path to start the agent method.

4.1.1 Build java agent project

Use maven to create a java_agent_demo project:

Create a new PreMainAgent class under the java folder:

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 包中定义的一个接口,也是这
个包的核心部分,
* 集中了其中几乎所有的功能方法,例如类定义的转换和操作等等。
类中提供两个静态方法,方法名均为premain,不能拼错。
在pom文件中添加打包插件:
* @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);
}
}

Two static methods are provided in the class. The method names are premain and cannot be misspelled.

Add the packaged plug-in in the pom file:

<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>com.itcast.PreMainAgent</Premain-Class>
                            <Agent-Class>com.itcast.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>

The plug-in will help us add agent-related configuration information when automatically generating the META-INF/MANIFEST.MF file.
Use maven's package command to package: After the
package is successful, copy the packaged jar package address.

4.1.2 Build the main project

Use maven to create the java_agent_user project:
Main class code:
Run it once, and then click Edit MAIN to start the class:

public class Main {
public static void main(String[] args) {
System.out.println("Hello World");
}
}

The code is

-javaagent: \ java-agent-demo-1.0-SNAPSHOT.jar = HELLOAGENT

Load javaagent at startup, point to the address of the java agent project jar package compiled in the previous section, and add the parameter HELLOAGENT at the end.
Run the MAIN method to view the results:

It can be seen that the code of the java agent has priority over the method of the MAIN function, which proves that the java agent is operating normally.

4.1.3 Statistic method call time

In Skywalking, the duration of each call is counted. In this section, we will use ByteBuddy and Java agent technology to count the call duration of the method.

-javaagent: \ java-agent-demo-1.0-SNAPSHOT.jar = HELLOAGENT

Byte Buddy is an open source library based on the Apache 2.0 license. It is dedicated to solving the complexity of bytecode manipulation and instrumentation API. The stated goal of Byte Buddy is to hide explicit bytecode operations behind a type-safe domain-specific language. By using Byte Buddy, anyone familiar with the Java programming language is expected to perform bytecode operations very easily. ByteBuddy provides additional APIs to generate Java agent, which can easily enhance our existing code.
Add dependency:

    <dependencies>

        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>1.10.16</version>
        </dependency>
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy-agent</artifactId>
            <version>1.10.16</version>
        </dependency>
    </dependencies>

Modify the code of PreMainAgent:

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()
            // 根据包名前缀拦截类
            .ignore(
                nameStartsWith("net.bytebuddy.")
                    .or(nameStartsWith("org.slf4j."))
                    .or(nameStartsWith("org.groovy."))
                    .or(nameContains("javassist"))
                    .or(nameContains(".asm."))
                    .or(nameContains(".reflectasm."))
                    .or(nameStartsWith("sun.reflect"))
                    .or(ElementMatchers.isSynthetic()))
            .type(ElementMatchers.nameStartsWith("org.springframework.kafka.core").or(ElementMatchers.nameStartsWith("org.apache.kafka")).or(ElementMatchers.nameStartsWith("com.itcast")))
            // 拦截到的类由transformer处理
            .transform(transformer)
            .installOn(inst);
    }

First generate a converter, ByteBuddy provides a dedicated converter for java agent. Use the builder object to create a converter by implementing the Transformer interface. The converter can configure the format of the interception method, such as using the name. In this example, all methods are intercepted and an interceptor class MyInterceptor is defined.
After the interceptor is created, an agent object can be constructed through Byte Buddy's AgentBuilder builder. AgentBuilder can
take effect on the specified package name prefix, and at the same time, you need to specify the converter object.

MyInterceptor class:

package com.itcast;

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) {
        System.out.println("MyInterceptor start...");
        long start = System.currentTimeMillis();
        try {
            //执行原方法
            try {
                if (callable == null) {
                    return null;
                }
                return callable.call();
            } catch (Exception e) {
                System.out.println("MyInterceptor----Exception" + e.toString());
                return null;
            }
        } finally {
            //打印调用时长
            System.out.println(
                method.getDeclaringClass() + "---" + method.getName() + ":" + (System.currentTimeMillis() - start)
                    + "ms");
            System.out.println("MyInterceptor end...");
        }
    }
}

MyInterceptor is an implementation of an interceptor, which counts the duration of the call. The method in the parameter is the reflected method object, and the callable is the calling object, and the original method can be executed through the callable.call() method. Repackage and execute the maven package command. Next, modify the main project code. The main project puts the Main class under the com.agent package. Modify the code content as:


package com.agent;
public class Main {
public static void main(String[] args) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello World");
}
}

Sleep for 1 second to make the demonstration effect of the statistical duration better. The result is displayed after executing the main method:

We used java agent and Byte Buddy to calculate the duration of the method without modifying the code. Skywalking's agent is also based on these technologies to realize the statistics of the call duration.

Please refer to the dark horse programmer documentation https://github.com/heheliu321/jvm/tree/master/java_agent_user

Guess you like

Origin blog.csdn.net/nmjhehe/article/details/114922036