2021-07-13 基于aws链路追踪方案

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021-07-13 基于aws链路追踪方案相关的知识,希望对你有一定的参考价值。

参考技术A 目前日志使用aop+logback生成。
存在问题
1.高并发下日志大量生成以至于无法确认哪条日志对应哪个用户的操作;
2.日志生成过于零散缺乏统一的生成格式;
所以目前尝试引入链路追踪的方式来定位异常。

我们当前服务是单体服务,为此引入第三方过于复杂。
其次当前服务的监控依赖于aws的cloudwatch,目前通过fluent-bit将pods中的日志上传处理,暂时不希望引入额外的监控系统
最终决定通过拦截器+修改日志生成方案来解决。

使用该方案生成日志对controller层的参数有限制,不能引入request,response等参数,如果一定需要使用,可以通过spring容器内读取。

tarceId: 当traceId使用ThreadLocal时,在rpc调用出现一个问题。
环境:k8s springboot okhttp retrofit2
当使用retrofit2 调用下游业务时发现thraceId丢失。
之前因为业务紧,选择了一个非常low的方案,在所有api接口上添加@Header Sring traceId
今天在在学习新知识时偶然发现更优雅的解决方案,特此记录。

核心: 用MDC替换ThreadLcoal

当使用MDH代替ThreadLocal ,终于将大面积接口调用的TraceId参数去掉了。

字节码基于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

以上是关于2021-07-13 基于aws链路追踪方案的主要内容,如果未能解决你的问题,请参考以下文章

Dubbo日志链路追踪TraceId选型

可视化全链路日志追踪

链路追踪和应用性能监控有哪些区别?

企业如何从 0 到 1 构建整套全链路追踪体系

基础篇丨链路追踪(Tracing)其实很简单

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