19. SpringCloudAlibaba 实践笔记:Sleuth项目集成

最简整合

引入依赖

在每个微服务(用户微服务 shop-user、商品微服务 shop-product、订单微服务 shop-order、网关服务 shop-gateway)下的 pom.xml 文件中添加如下 Sleuth 的依赖。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

修改网关应用配置

server:
  port: 10002
spring:
  application:
    name: server-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    sentinel:
      transport:
        port: 7777
        dashboard: 127.0.0.1:8888
      web-context-unify: false
      eager: true

    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods: "*"
            allowCredentials: true
            allowedHeaders: "*"
      discovery:
        locator:
          enabled: true
          route-id-prefix: gateway-

检验配置效果

启动服务后,在浏览器输入: http://localhost:10001/server-order/order/submit_order?userId=1&productId=1&count=1,查看控制台输出

  • 用户微服务
2024-09-23 18:10:40.979  INFO [server-user,bf0f01cbd91b8ffb,515697f8a589d5fb] 23932 --- [nio-8060-exec-4] c.s.u.controller.UserController  : 获取到的用户信息为:{"address":"China","id":1,"password":"123456","phone":"12345678901","username":"chan"}
  • 商品微服务
2024-09-23 18:10:40.993  INFO [server-product,bf0f01cbd91b8ffb,9c4ab721b57f34e9] 24220 --- [nio-8087-exec-5] c.s.p.controller.ProductController     : 更新商品库存传递的参数为: 商品id:1, 购买数量:1 
  • 订单微服务
2024-09-23 18:10:40.995  INFO [server-order,bf0f01cbd91b8ffb,6ad81ebaf2d13f66] 8532 --- [nio-8080-exec-7] c.s.o.service.impl.OrderServiceImpl    : 库存扣减成功
  • 网关服务
2024-09-23 18:10:40.777 DEBUG [server-gateway,,] 22548 --- [ctor-http-nio-3] o.s.c.s.instrument.web.TraceWebFilter    : Received a request to uri [/server-order/order/submit_order]
2024-09-23 18:10:40.782 DEBUG [server-gateway,,] 22548 --- [ctor-http-nio-3] o.s.c.s.instrument.web.TraceWebFilter    : Handled receive of span NoopSpan(bf0f01cbd91b8ffb/bf0f01cbd91b8ffb)
2024-09-23 18:10:40.964 DEBUG [server-gateway,,] 22548 --- [ctor-http-nio-5] ientBeanPostProcessor$TracingDoOnRequest : Handled send of the netty client span [NoopSpan(bf0f01cbd91b8ffb/6ad81ebaf2d13f66)] with parent [bf0f01cbd91b8ffb/bf0f01cbd91b8ffb]
2024-09-23 18:10:41.006 DEBUG [server-gateway,,] 22548 --- [ctor-http-nio-5] PostProcessor$AbstractTracingDoOnHandler : Handle receive of the netty client span [NoopSpan(bf0f01cbd91b8ffb/6ad81ebaf2d13f66)]
2024-09-23 18:10:41.009 DEBUG [server-gateway,,] 22548 --- [ctor-http-nio-3] o.s.c.s.instrument.web.TraceWebFilter    : Handled send of NoopSpan(bf0f01cbd91b8ffb/bf0f01cbd91b8ffb)

抽样采集数据

Sleuth 支持抽样采集数据。尤其是在高并发场景下,如果采集所有的数据,那么采集的数据量就太大了,非常耗费系统的性能。通常的做法是可以减少一部分数据量,特别是对于采用 Http 方式去发送采集数据,能够提升很大的性能。

Sleuth 可以采用如下方式配置抽样采集数据。

spring:
  sleuth:
    sampler:
      probability: 1.0
  • probability:设置采样的概率。这里设置为 1.0,表示对所有的请求进行追踪采样,即全采样。如果将采样率设置为 0.5,那么大约有一半的请求会被采样进行追踪。

追踪自定义线程池

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

在服务中开启异步线程池任务,需要在用户微服务shop-user的 UserStarter 启动类上添加 @EnableAsync 注解。

@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);
    }
}

在用户微服务 shop-user 的 UserService 接口中定义一个 asyncMethod()方法

void asyncMethod();

在用户微服务 shop-user 的 UserServiceImpl 类中实现 asyncMethod()方法,并在 asyncMethod()方法上添加@Async 注解

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

在用户微服务 shop-user 的 UserController 类中新增 asyncApi()方法

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

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

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

  • 用户微服务
2024-09-23 18:26:51.993  INFO [server-user,37660d0bbaffd46b,2334d815f06a4f79] 27344 --- [nio-8061-exec-3] c.shop.user.controller.UserController  : 执行异步任务开始...
2024-09-23 18:26:52.005  INFO [server-user,37660d0bbaffd46b,2334d815f06a4f79] 27344 --- [nio-8061-exec-3] c.s.user.service.impl.UserServiceImpl  : 执行了异步任务...
2024-09-23 18:26:52.006  INFO [server-user,37660d0bbaffd46b,2334d815f06a4f79] 27344 --- [nio-8061-exec-3] c.shop.user.controller.UserController  : 异步任务执行结束...
  • 网关服务
2024-09-23 18:26:51.963 DEBUG [server-gateway,,] 23000 --- [ctor-http-nio-2] o.s.c.s.instrument.web.TraceWebFilter    : Received a request to uri [/server-user/user/async/api]
2024-09-23 18:26:51.963 DEBUG [server-gateway,,] 23000 --- [ctor-http-nio-2] o.s.c.s.instrument.web.TraceWebFilter    : Handled receive of span NoopSpan(37660d0bbaffd46b/37660d0bbaffd46b)
2024-09-23 18:26:51.968 DEBUG [server-gateway,,] 23000 --- [ctor-http-nio-7] ientBeanPostProcessor$TracingDoOnRequest : Handled send of the netty client span [NoopSpan(37660d0bbaffd46b/2334d815f06a4f79)] with parent [37660d0bbaffd46b/37660d0bbaffd46b]
2024-09-23 18:26:52.017 DEBUG [server-gateway,,] 23000 --- [ctor-http-nio-7] PostProcessor$AbstractTracingDoOnHandler : Handle receive of the netty client span [NoopSpan(37660d0bbaffd46b/2334d815f06a4f79)]
2024-09-23 18:26:52.017 DEBUG [server-gateway,,] 23000 --- [ctor-http-nio-2] o.s.c.s.instrument.web.TraceWebFilter    : Handled send of NoopSpan(37660d0bbaffd46b/37660d0bbaffd46b)

自定义链路过滤器

在 Sleuth 中存在链路过滤器,并且还支持自定义链路过滤器。TracingFilter 是 Sleuth 中负责处理请求和响应的组件,可以通过注册自定义的 TracingFilter 实例来实现一些扩展性的需求。

在用户微服务shop-user 创建 MyGenericFilter 类,继承 org.springframework.web.filter.GenericFilterBean 类。

@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().traceId();
            httpServletRequest.setAttribute("traceId", traceId);
            httpServletResponse.addHeader("SLEUTH-HEADER", traceId);
        }
        chain.doFilter(httpServletRequest, httpServletResponse);
    }
}
  • 在过滤器中,会将生成的 traceId 添加到 request 的 attribute ,并将 response 的 Header 添加 SLEUTH-HEADER=<traceId>

在用户微服务 shop-user 的 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";
}

在浏览器中输入 http://localhost:10001/server-user/user/sleuth/filter/api,查看用户微服务的输出信息

2024-09-23 19:17:20.720  INFO [server-user,4729363c365580bc,ebcf4a7cc2083793] 26388 --- [nio-8061-exec-1] c.shop.user.controller.UserController  : 获取到的traceId为: 4729363c365580bc

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

posted @ 2024-11-06 23:37  Jacob-Chen  阅读(281)  评论(0)    收藏  举报