【字节码】基于JavaAgent的全链路监控五- ThreadLocal链路追踪

在这里插入图片描述

1.概述

转载:基于JavaAgent的全链路监控五《ThreadLocal链路追踪》

Google开源的Dapper链路追踪组件,并在2010年发表了论文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》,这篇文章是业内实现链路追踪的标杆和理论基础,具有非常大的参考价值。目前,链路追踪组件有Google的Dapper,Twitter 的Zipkin,以及阿里的Eagleeye (鹰眼)等,它们都是非常优秀的链路追踪开源组件。本文主要讲述如何在Spring Cloud Sleuth中集成Zipkin。在Spring Cloud Sleuth中集成Zipkin非常的简单,只需要引入相应的依赖和做相关的配置即可。

链路追踪Dapper 当业务程序代码在线上运行时,实例A、实例B、实例C,他们直接可能从上到下依次调用,为了能很好的监控程序的调用链路,我们需要对调用链路进行追踪监控。实例的外部可能是通过RPC、HTTP、SOCKET、WEBSERVICE等方式进行调用,内部是方法逻辑依次执行。外部例如http可以通过在头部写入追踪ID进行监控,内部使用threadlocal进行保存上下文关系。{ThreadLocal变量特殊的地方在于:对变量值的任何操作实际都是对这个变量在线程中的一份copy进行操作,不会影响另外一个线程中同一个ThreadLocal变量的值。}

2.环境准备

IntelliJ IDEA Community Edition 2019
jdk1.8.0_45 64位

3. #配置信息(路径相关修改为自己的)

配置位置:Run/Debug Configurations -> VM options
配置内容:-javaagent:-javaagent:/Users/lcc/IdeaProjects/lcc_work/test-byte-buddy/byte-buddy-v1x-javaagent-demo3/target/byte-buddy-v1x-javaagent-demo3-1.0-SNAPSHOT.jar=test

4.maven设置如下



    <properties>
        <asm.version>9.0</asm.version>
        <slf4j-api.version>1.7.28</slf4j-api.version>
        <byte-buddy.version>1.12.11</byte-buddy.version>
<!--        <byte-buddy.version>1.8.20</byte-buddy.version>-->
        <fastjson.version>1.2.76</fastjson.version>
        <lombok.version>1.18.16</lombok.version>
    </properties>

    <dependencies>

        <!-- 日志 相关 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j-api.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j-api.version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>

        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>${byte-buddy.version}</version>
        </dependency>
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy-agent</artifactId>
            <version>${byte-buddy.version}</version>
        </dependency>

        <dependency>
            <groupId>com.bytebuddy</groupId>
            <artifactId>byte-buddy-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>


    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <addClasspath>true</addClasspath>
<!--                            <Premain-Class>com.javaagent.bytebuddy.MyPreMainAgent</Premain-Class>-->
<!--                            <Premain-Class>com.javaagent.bytebuddy.demo.MyAgent</Premain-Class>-->
                            <Premain-Class>com.javaagent.bytebuddy.demo4.TraceAgent</Premain-Class>
                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                            <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>


5.主要类

TrackManager 设置如下

package com.javaagent.bytebuddy.demo4;

import java.util.Stack;

public class TrackManager {
    
    

    private static final ThreadLocal<Stack<String>> track = new ThreadLocal<Stack<String>>();

    private static String createSpan() {
    
    
        Stack<String> stack = track.get();
        if (stack == null) {
    
    
            stack = new Stack<>();
            track.set(stack);
        }
        String linkId;
        if (stack.isEmpty()) {
    
    
            linkId = TrackContext.getLinkId();
            if (linkId == null) {
    
    
                linkId = "nvl";
                TrackContext.setLinkId(linkId);
            }
        } else {
    
    
            linkId = stack.peek();
            TrackContext.setLinkId(linkId);
        }
        return linkId;
    }

    public static String createEntrySpan() {
    
    
        String span = createSpan();
        Stack<String> stack = track.get();
        stack.push(span);
        return span;
    }


    public static String getExitSpan() {
    
    
        Stack<String> stack = track.get();
        if (stack == null || stack.isEmpty()) {
    
    
            TrackContext.clear();
            return null;
        }
        return stack.pop();
    }

    public static String getCurrentSpan() {
    
    
        Stack<String> stack = track.get();
        if (stack == null || stack.isEmpty()) {
    
    
            return null;
        }
        return stack.peek();
    }


}

TrackContext设置如下

package com.javaagent.bytebuddy.demo4;

/**
 * @author lcc
 */
public class TrackContext {
    
    

    private static final ThreadLocal<String> trackLocal = new ThreadLocal<String>();

    public static void clear(){
    
    
        trackLocal.remove();
    }

    public static String getLinkId(){
    
    
        return trackLocal.get();
    }

    public static void setLinkId(String linkId){
    
    
        trackLocal.set(linkId);
    }

}


TraceAdvice设置如下

package com.javaagent.bytebuddy.demo4;

import net.bytebuddy.asm.Advice;

import java.util.UUID;

public class TraceAdvice {
    
    


    @Advice.OnMethodEnter()
    public static void enter(@Advice.Origin("#t") String className, @Advice.Origin("#m") String methodName) {
    
    
        String linkId = TrackManager.getCurrentSpan();
        if (null == linkId) {
    
    
            linkId = UUID.randomUUID().toString();
            TrackContext.setLinkId(linkId);
        }
        String entrySpan = TrackManager.createEntrySpan();
        System.out.println("链路追踪:" + entrySpan + " " + className + "." + methodName);

    }

    @Advice.OnMethodExit()
    public static void exit(@Advice.Origin("#t") String className, @Advice.Origin("#m") String methodName) {
    
    
        TrackManager.getExitSpan();
    }


}

TraceAgent设置如下

package com.javaagent.bytebuddy.demo4;

import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.NamedElement;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;

import java.lang.instrument.Instrumentation;

public class TraceAgent {
    
    

    //JVM 首先尝试在代理类上调用以下方法
    public static void premain(String agentArgs, Instrumentation inst) {
    
    
        System.out.println("基于javaagent链路追踪");

        AgentBuilder agentBuilder = new AgentBuilder.Default();


        AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> {
    
    
            builder = builder.visit(
                    Advice.to(TraceAdvice.class)
                            .on(ElementMatchers.isMethod()
                                    /**
                                     * 测试发现,这里需要所有,不能做下面的改造 否则没有相关的效果
                                     */
                                    .and(ElementMatchers.any())
                                    // 这里相比原来的做了改造 我们只看我们这个包下的
//                                    .and(ElementMatchers.nameStartsWith("com.javaagent.bytebuddy.demo4_1"))
                                    .and(ElementMatchers.not(ElementMatchers.nameStartsWith("main")))));
            return builder;
        };

        /**
         * 这里要写我们的目标类,不能包括自身 TraceAgent 所在包,否则会报错如下
         * "Thread-0" java.lang.StackOverflowError
         * 	at com.javaagent.bytebuddy.demo4.TrackManager.getCurrentSpan(TrackManager.java:47)
         *
         */
        ElementMatcher.Junction<NamedElement> namedElementJunction = ElementMatchers.nameStartsWith("com.javaagent.bytebuddy.demo4_1");
        AgentBuilder.Identified.Extendable transform = agentBuilder.type(namedElementJunction).transform(transformer);
        // 1.12.11 版本使用这个
        agentBuilder = transform.asTerminalTransformation();
        // 1.8.20 版本使用这个
//        agentBuilder = transform.asDecorator();

        //监听
        AgentBuilder.Listener listener = new AgentBuilder.Listener() {
    
    
            @Override
            public void onDiscovery(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {
    
    

            }

            @Override
            public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b, DynamicType dynamicType) {
    
    
                System.out.println("onTransformation:" + typeDescription);
            }

            @Override
            public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b) {
    
    

            }

            @Override
            public void onError(String s, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable) {
    
    

            }

            @Override
            public void onComplete(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {
    
    

            }

        };

        agentBuilder.with(listener).installOn(inst);

    }

    //如果代理类没有实现上面的方法,那么 JVM 将尝试调用该方法
    public static void premain(String agentArgs) {
    
    
    }

}

6.测试类

这里注意测试类的包名称和主要类的名称不一致,不在一个包下,否则会报错

package com.javaagent.bytebuddy.demo4_1;


public class TraceAgentTest {
    
    

    public static void main(String[] args) {
    
    

        //线程一
        new Thread(() -> new TraceAgentTest().http_lt1()).start();

        //线程二
        new Thread(() -> {
    
    
            new TraceAgentTest().http_lt1();
        }).start();
    }


    public void http_lt1() {
    
    
        System.out.println("测试结果:hi1");
        http_lt2();
    }

    public void http_lt2() {
    
    
        System.out.println("测试结果:hi2");
        http_lt3();
    }

    public void http_lt3() {
    
    
        System.out.println("测试结果:hi3");
    }


}

7.运行结果

基于javaagent链路追踪
onTransformation:class com.javaagent.bytebuddy.demo4_1.TraceAgentTest
链路追踪:d9192c02-0fc7-437c-a47f-c150361dca19 com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt1
测试结果:hi1
链路追踪:7fde1ae3-95a0-4a34-a0d6-06fcbe0e596e com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt1
测试结果:hi1
链路追踪:d9192c02-0fc7-437c-a47f-c150361dca19 com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt2
测试结果:hi2
链路追踪:d9192c02-0fc7-437c-a47f-c150361dca19 com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt3
测试结果:hi3
链路追踪:7fde1ae3-95a0-4a34-a0d6-06fcbe0e596e com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt2
测试结果:hi2
链路追踪:7fde1ae3-95a0-4a34-a0d6-06fcbe0e596e com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt3
测试结果:hi3

猜你喜欢

转载自blog.csdn.net/qq_21383435/article/details/125570144