spring-cloud-sleuth 3.x的版本传递traceId和自定义属性

目录

要做的目标

思路

实现

A.依赖

B.代码编写

C:使用功能方增加依赖上上一步封装的jar包

模拟测试


为了简化后续做类似功能的童鞋操作,故整理一篇针对traceId和自定义属性全链路传递的处理操作,整体调试流程还是耽误了不少时间的。

要做的目标

实现全链路traceId传递,自定义全链路traceUrl标记(traceUrl的规则是比如:比如A-》B-》 C 这样得到的就是 traceUrl=/A/B/C)

思路

1.依赖sleuth传递各种信息 ,使用方式遵循它的规则

2.feign调用处增加处理标记逻辑

3.web服务入口处增加拦截处理标记逻辑

4.异步线程池处理逻辑(sleuth自带功能)

实现

1.项目封装一个基础的jar包,比如trace-jar

A.依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <!-- Import dependency management from Spring Boot -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

B.代码编写

//AOP处理feign逻辑前

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.util.StringUtils;


@Aspect
public class FeignClientTraceAop {


    @Around("@within(org.springframework.cloud.openfeign.FeignClient)")
    Object around(final ProceedingJoinPoint pjp) throws Throwable {
        String traceUrl = Tracer.getBaggage(TraceConstants.TRACE_KEY_TRACE_URL);
        if (!StringUtils.hasText(traceUrl)) {
            Tracer.createBaggage(TraceConstants.TRACE_KEY_TRACE_URL, TraceConstants.DELIMITER + TraceConstants.getLocalAppName());
        }
        return pjp.proceed();
    }
}

//常量类

import org.springframework.util.StringUtils;


public final class TraceConstants {
    public static final String TRACE_KEY_TRACE_ID = "traceId";
    public static final String TRACE_KEY_TRACE_URL = "traceUrl";
    public static final String DELIMITER = "/";
    public static final String APP_NAME_KEY = "appcode";
    public static final String APP_NAME_DEFAULT = "MIS_APP_NAME";
    private static String appName = null;


    public static String getLocalAppName() {
        if (appName != null) {
            return appName;
        }
        appName = FrameProperty.get(APP_NAME_KEY);
        if (StringUtils.hasText(appName)) {
            return appName;
        }
        appName = APP_NAME_DEFAULT;
        return appName;
    }

//入口过滤器处理

import org.springframework.util.StringUtils;

import org.slf4j.MDC;
import javax.servlet.*;
import java.io.IOException;


public class TraceExtFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        ///拦截一下把自己的 APPName处理掉
        String traceUrl = Tracer.getBaggage(TraceConstants.TRACE_KEY_TRACE_URL);
        String content;
        if (!StringUtils.hasText(traceUrl)) {
            content = TraceConstants.DELIMITER + TraceConstants.getLocalAppName();
        } else {
            content = traceUrl + TraceConstants.DELIMITER + TraceConstants.getLocalAppName();
        }
        Tracer.createBaggage(TraceConstants.TRACE_KEY_TRACE_URL, content);
        MDC.put(TraceConstants.TRACE_KEY_TRACE_URL, content);        
        filterChain.doFilter(servletRequest, servletResponse);
    }

//提供个Tracer静态接口,简化使用,容器不启动,不要直接使用否则逸出

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.cloud.sleuth.Span;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;


    public class Tracer implements ApplicationContextAware {


        private static final Logger logger = LoggerFactory.getLogger(com.qhxc.commons.trace.Tracer.class);


        private static org.springframework.cloud.sleuth.Tracer sleuthTracer;




        public static String getTraceId() {
            Span span = sleuthTracer.currentSpan();
            return span.context().traceId();
        }


        public static String getSpanId() {
            return sleuthTracer.currentSpan().context().spanId();
        }


        public static void createBaggage(String key, String value) {
            sleuthTracer.createBaggage(key, value);
        }


        public static String getBaggage(String key) {
            return sleuthTracer.getBaggage(key).get();
        }


        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            try {
                org.springframework.cloud.sleuth.Tracer bean = applicationContext.getBean(org.springframework.cloud.sleuth.Tracer.class);
                this.sleuthTracer = bean;
            } catch (BeansException e) {
                logger.error("初始化trace功能异常,请检查org.springframework.cloud.sleuth.Tracer注入情况", e);
                throw e;
            }
        }

//自动化注入类

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cloud.sleuth.autoconfig.instrument.web.SleuthWebProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;


@Configuration
public class TraceAutoConfiguration {


    @Bean
    Tracer delegateInnerTracer() {
        return new Tracer();
    }


    @Bean
    FeignClientTraceAop feignClientTraceAop() {
        return new FeignClientTraceAop();
    }

    @Bean
    public FilterRegistrationBean traceExtFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        TraceExtFilter filter = new TraceExtFilter();
        registration.setFilter(filter);
        registration.addUrlPatterns("/*");
        registration.setName("trace-ext-filter");
        registration.setOrder(SleuthWebProperties.TRACING_FILTER_ORDER + 1);
        return registration;
    }
}

//增加配置属性--这个点可以通过代码注入或者写到配置文件加载进去。具体实现可协商自己团队内部实现方式。

// 注释:这两个配置属性就是用来传递和一以及可以写入到MDC 的

spring.sleuth.baggage.correlation-fields=traceUrl
spring.sleuth.baggage-keys=traceUrl

C:使用功能方增加依赖上上一步封装的jar包

//此步骤省略,看团队内部如何共享jar包,比如放到nexus(仓库)上公用,或者其他的使用方式。

理论上上边操作不需要使用方做任何操作,依赖上上边实现的对应功能即可。

模拟测试

假定一个server端提供接口,一个client端调用(feign),然后通过浏览器调用client。

形成链路:前端---》  client----> server

1.server端实现

提供一个简单接口(内部假定有线程池异步处理)

(注意点:线程池对象必须托管到spring容器内部才可传递)

2.client端实现

提供一个api接口  /test/feign,然后调用server端的feign接口

3.效果请求

前端请求url:http://localhost:7777/test/fegin

客户端日志显示:

 server端日志显示:

INFO :2021-07-16 11:22:27.473[192.168.1.10][http-nio-8060-exec-1][DirectJDKLog.java:173]o.a.c.c.C.[Tomcat].[localhost].[/] traceid[] traceUrl[] -Initializing Spring DispatcherServlet 'dispatcherServlet'
INFO :2021-07-16 11:22:27.473[192.168.1.10][http-nio-8060-exec-1][FrameworkServlet.java:525]o.s.web.servlet.DispatcherServlet traceid[] traceUrl[] -Initializing Servlet 'dispatcherServlet'
INFO :2021-07-16 11:22:27.477[192.168.1.10][http-nio-8060-exec-1][FrameworkServlet.java:547]o.s.web.servlet.DispatcherServlet traceid[] traceUrl[] -Completed initialization in 4 ms
INFO :2021-07-16 11:22:28.842[192.168.1.10][http-nio-8060-exec-2][MessageController.java:36]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign] -span-traceId:79c00c369592d43a
INFO :2021-07-16 11:22:28.842[192.168.1.10][http-nio-8060-exec-2][MessageController.java:37]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign] -span-spanId:10dc2d03bc0bea8f
INFO :2021-07-16 11:22:28.845[192.168.1.10][http-nio-8060-exec-2][MessageController.java:66]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign] -traceId:null
INFO :2021-07-16 11:22:28.845[192.168.1.10][simpleTaskExecutor-1][MessageController.java:40]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -thread1:simpleTaskExecutor-1
INFO :2021-07-16 11:22:28.845[192.168.1.10][simpleTaskExecutor-2][MessageController.java:53]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -thread2:simpleTaskExecutor-2
INFO :2021-07-16 11:22:28.845[192.168.1.10][http-nio-8060-exec-2][MessageController.java:67]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign] -traceUrl:/test-feign/notification-message
INFO :2021-07-16 11:22:28.845[192.168.1.10][simpleTaskExecutor-1][MessageController.java:41]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -span1-traceId:79c00c369592d43a
INFO :2021-07-16 11:22:28.846[192.168.1.10][simpleTaskExecutor-2][MessageController.java:54]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -span2-traceId:79c00c369592d43a
INFO :2021-07-16 11:22:28.846[192.168.1.10][simpleTaskExecutor-1][MessageController.java:42]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -span1-spanId:066d1273f201f91c
INFO :2021-07-16 11:22:28.846[192.168.1.10][simpleTaskExecutor-2][MessageController.java:55]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -span2-spanId:2aded07e33b5cfa9
INFO :2021-07-16 11:22:28.846[192.168.1.10][simpleTaskExecutor-1][MessageController.java:45]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -111traceId:null
INFO :2021-07-16 11:22:28.846[192.168.1.10][simpleTaskExecutor-2][MessageController.java:58]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -2222traceId:null
INFO :2021-07-16 11:22:28.846[192.168.1.10][simpleTaskExecutor-1][MessageController.java:46]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -111traceUrl:/test-feign/notification-message
INFO :2021-07-16 11:22:28.846[192.168.1.10][simpleTaskExecutor-2][MessageController.java:59]c.q.c.x.n.c.MessageController traceid[79c00c369592d43a] traceUrl[/test-feign/notification-message] -222traceUrl:/test-feign/notification-message
INFO :2021-07-16 11:22:28.862[192.168.1.10][http-nio-8060-exec-2][AuthUtil.java:31]c.q.cloud.xch.notice.utils.AuthUtil traceid[79c00c369592d43a] traceUrl[/test-feign] -验证签名调用方签名:CrBAUzw+sRNfOeggxo5mwEhUR60=
INFO :2021-07-16 11:22:28.904[192.168.1.10][http-nio-8060-exec-2][TokenUtil.java:48]c.q.cloud.xch.notice.utils.TokenUtil traceid[79c00c369592d43a] traceUrl[/test-feign] -生成签名result=CrBAUzw+sRNfOeggxo5mwEhUR60=
INFO :2021-07-16 11:22:28.905[192.168.1.10][http-nio-8060-exec-2][AuthUtil.java:33]c.q.cloud.xch.notice.utils.AuthUtil traceid[79c00c369592d43a] traceUrl[/test-feign] -验证签名新生成签名:CrBAUzw+sRNfOeggxo5mwEhUR60=
INFO :2021-07-16 11:22:28.938[192.168.1.10][http-nio-8060-exec-2][AbstractAopLog.java:35]c.q.c.x.n.log.aoplog.ServiceAopLog traceid[79c00c369592d43a] traceUrl[/test-feign] -M:MessageServiceImpl.acceptMessage P:{"funCode":"SMS_MAIL_001","funSecret":"TZ51QXHVCuilmLw8","params":[{"key":"email","value":"laowang@xxxx.com"},{"key":"mobile","value":"18500198888"}],"userId":123,"areaCode":"ZH","uniqueId":"ABC-1626253170864","authToken":"CrBAUzw+sRNfOeggxo5mwEhUR60=","timeStamp":1626405748769} R: {"code":0,"msg":"ok","data":{"messageId":"43"}}
INFO :2021-07-16 11:22:28.939[192.168.1.10][http-nio-8060-exec-2][AbstractAopLog.java:35]c.q.c.x.n.l.aoplog.ControllerAopLog traceid[79c00c369592d43a] traceUrl[/test-feign] -M:MessageController.sendEventMessage P:{"funCode":"SMS_MAIL_001","funSecret":"TZ51QXHVCuilmLw8","params":[{"key":"email","value":"laowang@xxxx.com"},{"key":"mobile","value":"18500198888"}],"userId":123,"areaCode":"ZH","uniqueId":"ABC-1626253170864","authToken":"CrBAUzw+sRNfOeggxo5mwEhUR60=","timeStamp":1626405748769} R: {"code":0,"msg":"ok","data":{"messageId":"43"}}

以上就实现了traceUrl的串联服务调用功能,以上功能仅为了测试,若应用到企业级服务还需要封装处理。一般可以实现,类似标记传递后,做根据标记流量负载分流控制,服务治理环境标记,限制版本,监控注册客户端等,还有日志类功能记录等等。

注意点:logback日志配置注意点

参考网上的:%X{traceId}::%X{X-B3-TraceId:-} %X{traceUrl}

X-B3-TraceId 在这个版本视乎是读取不到的,各种测试都没有;

断点接口:ch.qos.logback.classic.pattern.MDCConverter#convert 看不到这个变量只有traceId和spanId

参考:

https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/html/project-features.html#features-baggage-vs-tags


版权声明:本文为ruishenh原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。