SA实战 ·《SpringCloud Alibaba实战》第16章-链路追踪:项目整合Sleuth实现链路追踪 下

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
应用实时监控服务-可观测链路OpenTelemetry版,每月50GB免费额度
简介: SA实战 ·《SpringCloud Alibaba实战》第16章-链路追踪:项目整合Sleuth实现链路追踪

追踪自定义线程池

Sleuth支持对异步任务的链路追踪,在项目中使用@Async注解开启一个异步任务后,Sleuth会为异步任务重新生成一个Span。但是如果使用了自定义的异步任务线程池,则会导致Sleuth无法新创建一个Span,而是会重新生成Trace和Span。此时,需要使用Sleuth提供的LazyTraceExecutor类来包装下异步任务线程池,才能在异步任务调用链路中重新创建Span。

在服务中开启异步线程池任务,需要使用@EnableAsync。所以,在演示示例前,先在用户微服务shop-user的io.binghe.shop.UserStarter启动类上添加@EnableAsync注解,如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description 启动用户服的类
 */
@SpringBootApplication
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan(value = { "io.binghe.shop.user.mapper" })
@EnableDiscoveryClient
@EnableAsync
public class UserStarter {
    public static void main(String[] args){
        SpringApplication.run(UserStarter.class, args);
    }
}


演示使用@Async注解开启任务

(1)在用户微服务shop-user的io.binghe.shop.user.service.UserService接口中定义一个asyncMethod()方法,如下所示。

void asyncMethod();

(2)在用户微服务shop-user的io.binghe.shop.user.service.impl.UserServiceImpl类中实现asyncMethod()方法,并在asyncMethod()方法上添加@Async注解,如下所示。

@Async
@Override
public void asyncMethod() {
    log.info("执行了异步任务...");
}

(3)在用户微服务shop-user的io.binghe.shop.user.controller.UserController类中新增asyncApi()方法,如下所示。

@GetMapping(value = "/async/api")
public String asyncApi() {
    log.info("执行异步任务开始...");
    userService.asyncMethod();
    log.info("异步任务执行结束...");
    return "asyncApi";
}

(4)分别启动用户微服务和网关服务,在浏览器中输入链接http://localhost:10001/server-user/user/async/api

图片.pngimage.gif

(5)查看用户微服务与网关服务的控制台日志,分别存在如下日志。

  • 用户微服务
[server-user,499d6c7128399ed0,a81bd920de0b07de,true]执行异步任务开始...
[server-user,499d6c7128399ed0,a81bd920de0b07de,true]异步任务执行结束...
[server-user,499d6c7128399ed0,e2f297d512f40bb8,true]执行了异步任务...
  • 网关服务
[server-gateway,499d6c7128399ed0,499d6c7128399ed0,true]

可以看到Sleuth为异步任务重新生成了Span。

演示自定义任务线程池

在演示使用@Async注解开启任务的基础上继续演示自定义任务线程池,验证Sleuth是否为自定义线程池新创建了Span。

(1)在用户微服务shop-user中新建io.binghe.shop.user.config包,在包下创建ThreadPoolTaskExecutorConfig类,继承org.springframework.scheduling.annotation.AsyncConfigurerSupport类,用来自定义异步任务线程池,代码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description Sleuth异步线程池配置
 */
@Configuration
@EnableAutoConfiguration
public class ThreadPoolTaskExecutorConfig extends AsyncConfigurerSupport {
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(10);
        executor.setThreadNamePrefix("trace-thread-");
        executor.initialize();
        return executor;
    }
}

(2)以debug的形式启动用户微服务和网关服务,并在io.binghe.shop.user.config.ThreadPoolTaskExecutorConfig#getAsyncExecutor()方法中打上断点,如下所示。

图片.png

可以看到,项目启动后并没有进入io.binghe.shop.user.config.ThreadPoolTaskExecutorConfig#getAsyncExecutor()方法,说明项目启动时,并不会创建异步任务线程池。


(3)在浏览器中输入链接http://localhost:10001/server-user/user/async/api,此时可以看到程序已经执行到io.binghe.shop.user.config.ThreadPoolTaskExecutorConfig#getAsyncExecutor()方法的断点位置。

图片.png

说明异步任务线程池是在调用了异步任务的时候创建的。

接下来,按F8跳过断点继续运行程序,可以看到浏览器上的显示结果如下。

图片.pngimage.gif

(4)查看用户微服务与网关服务的控制台日志,分别存在如下日志。

  • 用户微服务
[server-user,f89f2355ec3f9df1,4d679555674e96a4,true]执行异步任务开始...
[server-user,f89f2355ec3f9df1,4d679555674e96a4,true]异步任务执行结束...
[server-user,0ee48d47e58e2a42,0ee48d47e58e2a42,true]执行了异步任务...
  • 网关服务
[server-gateway,f89f2355ec3f9df1,f89f2355ec3f9df1,true]

可以看到,使用自定义异步任务线程池时,在用户微服务中在执行异步任务时,重新生成了Trace和Span。

注意对比用户微服务中输出的三条日志信息,最后一条日志信息的TraceID和SpanID与前两条日志都不同。


演示包装自定义线程池

在自定义任务线程池的基础上继续演示包装自定义线程池,验证Sleuth是否为包装后的自定义线程池新创建了Span。

(1)在用户微服务shop-user的io.binghe.shop.user.config.ThreadPoolTaskExecutorConfig类中注入BeanFactory,并在getAsyncExecutor()方法中使用org.springframework.cloud.sleuth.instrument.async.LazyTraceExecutor()来包装返回的异步任务线程池,修改后的io.binghe.shop.user.config.ThreadPoolTaskExecutorConfig类的代码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description Sleuth异步线程池配置
 */
@Configuration
@EnableAutoConfiguration
public class ThreadPoolTaskExecutorConfig extends AsyncConfigurerSupport {
    @Autowired
    private BeanFactory beanFactory;
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(10);
        executor.setThreadNamePrefix("trace-thread-");
        executor.initialize();
        return new LazyTraceExecutor(this.beanFactory, executor);
    }
}

(2)分别启动用户微服务和网关服务,在浏览器中输入链接http://localhost:10001/server-user/user/async/api

图片.pngimage.gif

(3)查看用户微服务与网关服务的控制台日志,分别存在如下日志。

  • 用户微服务
[server-user,157891cb90fddb65,0a278842776b1f01,true]执行异步任务开始...
[server-user,157891cb90fddb65,0a278842776b1f01,true]异步任务执行结束...
[server-user,157891cb90fddb65,1ba55fd3432b77ae,true]执行了异步任务...
  • 网关服务
[server-gateway,157891cb90fddb65,157891cb90fddb65,true]

可以看到Sleuth为异步任务重新生成了Span。


综上说明:Sleuth支持对异步任务的链路追踪,在项目中使用@Async注解开启一个异步任务后,Sleuth会为异步任务重新生成一个Span。但是如果使用了自定义的异步任务线程池,则会导致Sleuth无法新创建一个Span,而是会重新生成Trace和Span。此时,需要使用Sleuth提供的LazyTraceExecutor类来包装下异步任务线程池,才能在异步任务调用链路中重新创建Span。


自定义链路过滤器

在Sleuth中存在链路过滤器,并且还支持自定义链路过滤器。


自定义链路过滤器概述

TracingFilter是Sleuth中负责处理请求和响应的组件,可以通过注册自定义的TracingFilter实例来实现一些扩展性的需求。


演示自定义链路过滤器

本案例演示通过过滤器验证只有HTTP或者HTTPS请求才能访问接口,并且在访问的链接不是静态文件时,将traceId放入HttpRequest中在服务端获取,并在响应结果中添加自定义Header,名称为SLEUTH-HEADER,值为traceId。

(1)在用户微服务shop-user中新建io.binghe.shop.user.filter包,并创建MyGenericFilter类,继承org.springframework.web.filter.GenericFilterBean类,代码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description 链路过滤器
 */
@Component
@Order( Ordered.HIGHEST_PRECEDENCE + 6)
public class MyGenericFilter extends GenericFilterBean{
    private Pattern skipPattern = Pattern.compile(SleuthWebProperties.DEFAULT_SKIP_PATTERN);
    private final Tracer tracer;
    public MyGenericFilter(Tracer tracer){
        this.tracer = tracer;
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)){
            throw new ServletException("只支持HTTP访问");
        }
        Span currentSpan = this.tracer.currentSpan();
        if (currentSpan == null) {
            chain.doFilter(request, response);
            return;
        }
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = ((HttpServletResponse) response);
        boolean skipFlag = skipPattern.matcher(httpServletRequest.getRequestURI()).matches();
        if (!skipFlag){
            String traceId = currentSpan.context().traceIdString();
            httpServletRequest.setAttribute("traceId", traceId);
            httpServletResponse.addHeader("SLEUTH-HEADER", traceId);
        }
        chain.doFilter(httpServletRequest, httpServletResponse);
    }
}

(2)在用户微服务shop-user的io.binghe.shop.user.controller.UserController类中新建sleuthFilter()方法,在sleuthFilter()方法中获取并打印traceId,如下所示。

@GetMapping(value = "/sleuth/filter/api")
public String sleuthFilter(HttpServletRequest request) {
    Object traceIdObj = request.getAttribute("traceId");
    String traceId = traceIdObj == null ? "" : traceIdObj.toString();
    log.info("获取到的traceId为: " + traceId);
    return "sleuthFilter";
}

(3)分别启动用户微服务和网关服务,在浏览器中输入http://localhost:10001/server-user/user/sleuth/filter/api,如下所示。

图片.pngimage.gif

查看用户微服务的控制台会输出如下信息。

获取到的traceId为: f63ae7702f6f4bba

查看浏览器的控制台,看到在响应的结果信息中新增了一个名称为SLEUTH-HEADER,值为f63ae7702f6f4bba的Header,如下所示。

图片.png

说明使用Sleuth的过滤器可以处理请求和响应信息,并且可以在Sleuth的过滤器中获取到TraceID。


好了,今天我们就到儿吧,限于篇幅,文中并未给出完整的案例源代码,想要完整源代码的小伙伴可加入【冰河技术】知识星球获取源码。也可以加我微信:hacker_binghe,一起交流技术。

相关实践学习
基于OpenTelemetry构建全链路追踪与监控
本实验将带领您快速上手可观测链路OpenTelemetry版,包括部署并接入多语言应用、体验TraceId自动注入至日志以实现调用链与日志的关联查询、以及切换调用链透传协议以满足全链路打通的需求。
分布式链路追踪Skywalking
Skywalking是一个基于分布式跟踪的应用程序性能监控系统,用于从服务和云原生等基础设施中收集、分析、聚合以及可视化数据,提供了一种简便的方式来清晰地观测分布式系统,具有分布式追踪、性能指标分析、应用和服务依赖分析等功能。 分布式追踪系统发展很快,种类繁多,给我们带来很大的方便。但在数据采集过程中,有时需要侵入用户代码,并且不同系统的 API 并不兼容,这就导致了如果希望切换追踪系统,往往会带来较大改动。OpenTracing为了解决不同的分布式追踪系统 API 不兼容的问题,诞生了 OpenTracing 规范。OpenTracing 是一个轻量级的标准化层,它位于应用程序/类库和追踪或日志分析程序之间。Skywalking基于OpenTracing规范开发,具有性能好,支持多语言探针,无侵入性等优势,可以帮助我们准确快速的定位到线上故障和性能瓶颈。 在本套课程中,我们将全面的讲解Skywalking相关的知识。从APM系统、分布式调用链等基础概念的学习加深对Skywalking的理解,从0开始搭建一套完整的Skywalking环境,学会对各类应用进行监控,学习Skywalking常用插件。Skywalking原理章节中,将会对Skywalking使用的agent探针技术进行深度剖析,除此之外还会对OpenTracing规范作整体上的介绍。通过对本套课程的学习,不止能学会如何使用Skywalking,还将对其底层原理和分布式架构有更深的理解。本课程由黑马程序员提供。
相关文章
|
15天前
|
存储 监控 数据可视化
双十一线上服务调用链路追踪SkyWalking实战分析
【11月更文挑战第27天】随着电商行业的飞速发展,双十一购物节已成为全球最大的购物狂欢节之一。在双十一期间,电商平台需要处理海量的用户请求和订单,这对系统的稳定性和性能提出了极高的要求。为了确保系统在高并发环境下的稳定运行,对线上服务的调用链路进行追踪和分析显得尤为重要。本文将通过实战案例,详细介绍如何在双十一期间使用SkyWalking对线上服务进行调用链路追踪,并结合Seata实现分布式事务管理,从而保障系统的稳定性和性能。
33 5
|
4月前
|
数据可视化 Java Nacos
Sleuth+Zipkin 实现 SpringCloud 链路追踪
【8月更文挑战第9天】Sleuth+Zipkin 实现 SpringCloud 链路追踪
117 1
Sleuth+Zipkin 实现 SpringCloud 链路追踪
|
5月前
|
存储 监控 Java
zipkin 与 sleuth 实现链路追踪
zipkin 与 sleuth 实现链路追踪
75 0
|
6月前
|
存储 消息中间件 Java
【分布式链路追踪技术】sleuth+zipkin
【分布式链路追踪技术】sleuth+zipkin
77 2
|
7月前
|
消息中间件 SpringCloudAlibaba Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
1025 0
|
JSON 运维 监控
链路追踪Skywalking应用实战 2
链路追踪Skywalking应用实战
258 0
|
监控 Java BI
链路追踪Skywalking应用实战 1
链路追踪Skywalking应用实战
289 0
|
数据可视化 Java Docker
Sleuth微服务链路追踪整合ELK和zipkin
Sleuth微服务链路追踪整合ELK和zipkin
310 0
|
监控 网络协议 Java
分布式链路追踪- SkyWalking使用手册
分布式链路追踪- SkyWalking使用手册
1252 0
分布式链路追踪- SkyWalking使用手册
|
4月前
|
存储 监控 开发者
分布式链路监控系统问题之系统拆分后链路追踪技术的问题如何解决
分布式链路监控系统问题之系统拆分后链路追踪技术的问题如何解决