聊聊 SpringBoot3 的 Micrometer Tracing

4,707 阅读6分钟

这是个很尴尬的话题,为什么这么说呢?当我们在讨论卷和造轮子两个事情的时候,SpringBoot 的开发人员也正在做着同样的事情...

回顾

之前在 微服务-分布式链路 这篇文章中我介绍了 Dapper、OpenTracing,并以蚂蚁分布式链路组件 SOFATracer 为例,较为详细的说明了分布式链路组件中的一些技术细节和实现方式,有兴趣的同学可以自行查看。

Micrometer Tracing

最近关注 SpringBoot3 的发布,在 Production-ready Features 中,首次将 tracing 作为一个单独的功能项写进官方文档中;在此之前 SpringBoot 仅提供了 Http Tracing 的能力,并且默认情况下,是在内存中存储 近 100 次请求的记录;在 2.7.x 的官方文档中,Http Tracing 只是建议在开发环境使用,对于生产环境,官方文档也明确的指出,建议使用 Zipkin 或 Spring Cloud Sleuth 这类比较成熟的可观测性解决方案。

从 Dapper 到 OpenTracing,解决了厂商无关和统一链路 API 的问题,从 OpenTracing 到 OpenTelemetry,解决统一可观测性 API 的问题,并且 OpenTelemetry2019-5.7 被 CNCF Accepted 之后,也在不断地孵化和完善。如果说 SpringBoot 面向 OpenTelemetry 提供 Tracing,可能更便于接受,但是 SpringBoot 却使用了 Micrometer tracing,并且通过 Facade 进行了桥接 Brave 和 otel。

首先一点是,SpringBoot 将 Tracing 在 v3 系列提供出来比较容易理解,在可观测性上,tracing 一直都是 SpringBoot 缺失的,在 SpringCloud 中,它通过 Spring Cloud Slueth 整合了 Brave 补缺了这一环。

比较疑惑的是,SpringBoot 提供的 tracing 为什么会选择使用一个新的 facade Micrometer tracing 来实现,这一点我尝试从 issue 中去找答案,但也没有找到比较有说服力的,比如 Add auto-configuration for Micrometer 2.0 Observation APIAdd support for Micrometer tracing。不过有意思的是,在 spring.io/projects/sp… 这里给出了一些信息,意思就是 Spring Cloud Slueth 将迁移到 Micrometer tracing

那么关于为什么 SpringBoot 使用 Micrometer 而不是 OpenTelemetry,可能有以下几点原因(一家之言,欢迎指导):

  • 1、在之前的一些版本中,Micrometer 关注更多是 metrics,而 OpenTelemetry 则更多关注 tracing,但是随着版本的迭代完善,Micrometer 和 OpenTelemetry 在 metrics 和 tracing API 上基本都具备了。重要的是,在此之前,SpringBoot 已经对 Micrometer Metrics 进行了支持,所谓近水楼台先得月
  • 2、OpenTelemetry 的目标是厂商无关,语言无关,penTelemetry 更适合在异构技术栈中发挥作用而 Micrometer 一直以来都是基于 Java 语言,这与 Spring 体系从根上是一致的
  • 3、Micrometer API 进行大量更改。最重要的变化是引入了一个新的 API:Observation API,这样便于使用者能够使用统一的 API 来观测业务代码,包括 metrics, tracing 以及 logging。

下面就 SpringBoot3 Tracing 展开聊聊。

SpringBoot3 Tracing 剖析

SpringBoot3 在 Spring Boot Actuator 中为 Micrometer Tracing 的依赖性管理和自动配置。Micrometer Tracing 充当了类似日志领域内 slf4j 门面的角色。

Micrometer Tracing API

Micrometer Tracing 中的相关概念也是借用 Dapper 的,比如 SpanTrace 等,这里不做过多介绍。总体来说,Micrometer Tracing 包括以下几个部分:

  • 核心模块主要包含 instrumentation SPI
  • 用于适配其它 tracing 实现的 bridge 桥接器
  • 用于上报 span 数据的 reportor 扩展机制
  • 测试模块

功能划分和代码模块结构组织是一致的

image.png

在 Micrometer Tracing 中,一个完整的 tracing 大致如下图所示

image.png

bridge 桥接器

当前版本,Micrometer Tracing 支持两种 Tracers

在使用时,你只能选择一个 bridge 桥接实现,如果在你的 classpath 中包括两个,可能会有一些意想不到的问题,比如重复的 trace 或者 span。

Reporters

当前版本,Micrometer Tracing 支持两种 Reporters

如何和 Brave 集成的

不管是 brave 还是 otel,它们都是完整的分布式链路解决方案,包括 API 和 instrumentation;Micrometer Tracing 与 brave、otel 的集成是典型的 接口继承 + 委托 的形式实现的,以 span 为例:

import io.micrometer.tracing.Span;

/**
 * Brave implementation of a {@link Span}.
 *
 * @author Marcin Grzejszczak
 * @since 1.0.0
 */
public class BraveSpan implements Span {

    final brave.Span delegate;
    // 省略其它无关代码 ... 
}

这种方式也是 skywalking、zipkin-brave、sofatracer1.0、jaeger 等组件在早期支持 opentrcing API 的实现方式。

如何上报给 zipkin 的

我在 原理 | 分布式链路跟踪组件 SOFATracer 和 Zipkin 模型转换 这篇文章中介绍过 SOFATracer 是如何将 span 数据上报给 zipkin 的,它的做法是:在 span 结束的时候,通过预留的 reporter 扩展机制,使得用户可以通过自定义 reporter 来实现 span 数据的上报。两个关键点:

  • 上报时机:span 结束时
  • 上报方式:通过自定义 reporter 上报

Micrometer Tracing 本身包括了对 span 完整生命周期管理的 API

/**
 * Starts this span.
 * @return this span
 */
Span start();

/**
 * Ends the span. The span gets stopped and recorded if not noop.
 */
void end();

/**
 * Ends the span. The span gets stopped and recorded if not noop.
 * @param time timestamp
 * @param timeUnit time unit of the timestamp
 */
void end(long time, TimeUnit timeUnit);

因此上报时机都是一样的,在 span 结束时发生。上报方式,Brave 提供了一个 SpanHandler 扩展接口,对应的具体实现是 ZipkinSpanHandler,otel 中提供的是 SpanExporter 扩展接口,对应的实现是 ZipkinSpanExporter。下面用一张简单的图描述下

image.png

a simple guides for you

这里给一个小案例,主要是官方文档中并没有提供 step by step 的集成方式。以 Micrometer tacing + brave + zipkin 的方式为例。

依赖

  • 增加依赖,下面给出的依赖均为必须提供的
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId> spring-boot-starter-actuator</artifactId>
    </dependency>

    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-tracing</artifactId>
    </dependency>

    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-tracing-bridge-brave</artifactId>
    </dependency>
    <dependency>
        <groupId>io.zipkin.reporter2</groupId>
        <artifactId>zipkin-reporter-brave</artifactId>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-tracing-bom</artifactId>
            <version>1.0.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

提供一个测试 REST API

@RequestMapping("api")
@RestController()
public class MyController {
    private static final Log logger = LogFactory.getLog(MyController.class);
    @RequestMapping("test")
    public String test() {
        // 配置了在日志中输出 traceId
        logger.info("this is test log ....");
        return "SUCCESS";
    }
}

基本配置

# 采样比率
management.tracing.sampling.probability=1.0
# 将 traceId 和 spanId 和 log 绑定
logging.pattern.level=%5p [${spring.application.name:test},%X{traceId:-},%X{spanId:-}]`

测试

访问 curl http://localhost:8080/api/test

  • 日志输出如下:
2022-12-06T12:06:36.971+08:00  INFO 
[test,638ebfccd315f7022e7eee028343517f,2e7eee028343517f] 61043 --- [nio-8080-
exec-1] c.g.bridge.boot.controller.MyController  : this is test log ....
  • zipkin 界面

image.png

PS:你可能没有看到任何关于 zipkin 的配置,因为默认情况下,zipkin 的上报地址是 127.0.0.1:9411,我在本地通过 docker 启动了一个 zipkin 实例,所以我并不需要做额外的配置。

总结

本篇对 SpringBoot3 tracing 做了一些介绍,通过本篇文章,希望你可以了解到 SpringBoot3 中提供 tracing 的初衷,以及在众多事实标准存在情况下选择 Micrometer tracing 作为 API 的原因。这篇文章并不会涉及到过多对于 tracing 基础概念的介绍,如果你有兴趣可以自行查阅。最后我给出了 Micrometer tacing + brave + zipkin 一个小案例,希望可以帮助到你。

本文正在参加「金石计划 . 瓜分6万现金大奖」