在 Spring Boot 3 WebFlux 中实现跟踪的案例


这个存储库(点击标题)包含一个简单的演示应用程序,展示了 Micrometer Tracing 与 Kotlin 和 Spring Boot WebFlux 的用法。

包含以下功能:

跟踪是观察软件系统细节的好工具。正确完成后,它使开发人员能够了解应用程序中和应用程序之间发生不同交互的时间、地点和方式。它使观察复杂的软件系统变得容易得多。

Spring Boot 3开始,用于在 Spring Boot 中进行跟踪的旧Spring Cloud Sleuth解决方案将被新的Micrometer Tracing库取代。

Micrometer被用作默认解决方案来公开独立于平台的指标并监控基于 JVM 的微服务(例如Prometheus)。最新添加的功能通过独立于平台的跟踪解决方案扩展了 Micrometer 生态系统。这使开发人员能够使用一个通用 API 来检测他们的应用程序,并以不同的格式将其导出到跟踪收集器,如JaegerZipkinOpenTelemetry

定义端点
我们将从添加一个带有测试端点的简单 REST 控制器类开始,该端点使用Spring WebClient调用外部 API 。我们正在使用suspend关键字来使用 Kotlin 的Coroutines。这允许我们在使用 Spring WebFlux 的反应流的同时编写命令式代码。

在下面的示例中,我们使用 Spring WebClient 调用外部 TODO-API,它以 JSON 字符串的形式返回 TODO 项。我们还将创建一个日志消息,稍后应该包含一些跟踪信息。

@RestController
class Controller {
  val log = LoggerFactory.getLogger(javaClass)
  
  val webClient = WebClient.builder()
    .baseUrl("https://jsonplaceholder.typicode.com")
    .build()
  
  @GetMapping("/test")
  suspend fun test(): String {
    // simulate some complex calculation  
    delay(1.seconds)
    
    log.info("test log with tracing info")
    
    // make web client call to external API
    val externalTodos = webClient.get()
      .uri("/todos/1")
      .retrieve()
      .bodyToMono(String::class.java)
      .awaitSingle()
    
    return externalTodos
  }
}

添加Micrometer跟踪
在下一步,我们将在build.gradle.kts文件中添加Micrometer追踪的依赖项。由于Micrometer支持不同的追踪格式和供应商,所以依赖关系被分割开来,所以我们只导入我们需要的东西。为了使所有的依赖关系保持同步,我们使用Micrometer追踪系统的BOM(物料清单)。此外,我们还添加了核心的依赖关系和一个桥梁,将Micrometer的跟踪翻译成OpenTelemetry格式(其他格式也可使用)。

 implementation(platform("io.micrometer:micrometer-tracing-bom:1.0.0"))
 implementation("io.micrometer:micrometer-tracing")
 implementation("io.micrometer:micrometer-tracing-bridge-otel")

我们还需要添加一个导出器依赖项来导出创建的跟踪。对于此示例,我们将使用由 OpenTelemetry 维护并由 Micrometer Tracing 支持的 Zipkin 导出器。

implementation("io.opentelemetry:opentelemetry-exporter-zipkin")

配置
设置追踪所需的最后一件事是配置。它可以作为Spring application.yaml创建,位于src/main/resources下。

首先,我们必须在管理设置中启用追踪功能。我们还将把追踪的采样率设置为1(默认为0.1),以便为服务收到的每个调用创建追踪。在一个有大量请求的生产系统中,你可能只想跟踪一些调用。此外,我们还可以定义端点URL,让Zipkin导出器在那里发送跟踪信息。
最后,我们要更新默认的日志模式,包括追踪和跨度ID。

management:
  tracing:
    enabled: true
    sampling.probability: 1.0

  zipkin.tracing.endpoint: http://localhost:9411/api/v2/spans

logging.pattern.level: "trace_id=%mdc{traceId} span_id=%mdc{spanId} trace_flags=%mdc{traceFlags} %p"


查看
要查看调用端点时创建的实际跟踪,我们需要收集和查看它们。对于本教程,我们将使用Zipkin。但是,也可以使用其他系统,例如Grafana LokiDatadog
我们可以使用以下命令通过Docker在本地启动 Zipkin 实例:

docker run -p 9411:9411 openzipkin/zipkin

要检查 Zipkin 是否正在运行,http://localhost:9411/zipkin/请在浏览器中打开。这应该会向您显示 Zipkin UI。
现在您可以再次调用我们的 Spring Boot 服务的端点。之后,当您在 Zipkin 中搜索任何踪迹时,您应该能够找到端点请求的踪迹。

数据库追踪
典型的 Spring Boot 应用程序通常会连接到实际应用程序中的数据库。要利用反应式堆栈,建议使用R2DBC API 而不是JDBC
由于 Micrometer Tracing 非常新,因此目前没有可用的自动跟踪。然而,Spring 团队正在研究创建一个自动配置。可以在此处找到实验存储库。
对于我们的项目,我们将添加以下依赖项到build.gradle.kts. 为了便于测试设置,我们不会使用真实的数据库,而是使用H2 内存数据库

 implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
 runtimeOnly("com.h2database:h2")
 runtimeOnly("io.r2dbc:r2dbc-h2")

 // R2DBC micrometer auto tracing
 implementation("org.springframework.experimental:r2dbc-micrometer-spring-boot:1.0.2")

在我们的Kotlin代码中,我们添加了一个简单的具有coroutine支持的CRUD存储库。下面是它的样子。

@Table("todo")
data class ToDo(
  @Id
  val id: Long = 0,
  val title: String,
)

interface ToDoRepository : CoroutineCrudRepository<ToDo, Long>


@RestController
class Controller(
  val todoRepo: ToDoRepository,
  // ...
) {

  @GetMapping("/test")
  suspend fun test(): String {
    // ...
    
    // Sample traced DB call
    val dbtodos = todoRepo.findAll().toList()
    
    // ...

    return "${dbtodos.size} $externalTodos"
  }
}

调用我们的端点将导致多一个跨度被添加。这个名为query的新跨度包含多个标签,包括由Spring Data R2DBC执行的SQL查询。

结论
Micrometer 和新的跟踪扩展,统一了 Spring Boot 3 之后的可观察性堆栈。它对跨不同公司及其堆栈使用的不同跟踪解决方案进行了很好的抽象。因此,它简化了我们开发人员的工作。

在使用 Spring WebFlux 进行反应式编程方面,仍有一些改进的潜力,尤其是 Kotlin。Micrometer 团队正在与Project Reactor(Spring WebFlux 使用的反应式库)背后的团队进行积极对话,以简化 Micrometer Tracing 在反应式堆栈中的使用。