我的 OpenTelemetry Tracing 演示包含两个 Spring Boot 组件。其中一个使用 Java 代理,最近我将其从 v1.x 升级到 v2.x 时注意到了不同的行为。在另一个中,我使用 Micrometer Tracing,因为我编译为 GraalVM 原生,它无法处理 Java 代理。
在这篇文章中,我想比较这三种方法:Java agent v1、Java agent v2 和 Micrometer Tracing。
我将使用相同的基础应用程序:一个用 Kotlin 编码的简单 Spring Boot 应用程序。它提供单个端点。
entry()
intermediate()
的函数WebClient
实例( RestTemplate
的替代品)来调用上述端点entry()
函数找到它,它就不会继续执行
翻译过来就是下面的代码:
@SpringBootApplication class Agent1xApplication @RestController class MicrometerController { private val logger = LoggerFactory.getLogger(MicrometerController::class.java) @GetMapping("/{message}") fun entry(@PathVariable message: String, @RequestHeader("X-done") done: String?) { logger.info("entry: $message") if (done == null) intermediate() } fun intermediate() { logger.info("intermediate") RestClient.builder() .baseUrl("http://localhost:8080/done") .build() .get() .header("X-done", "true") .retrieve() .toBodilessEntity() } }
对于每个设置,我都会检查两个阶段:启用 OpenTelemetry 的主要阶段和创建额外内部跨度的自定义阶段。
Micrometer Tracing 源自Micrometer ,一种“与供应商无关的应用程序可观察性外观”。
Micrometer Tracing 为最流行的跟踪器库提供了一个简单的外观,让您可以对基于 JVM 的应用程序代码进行插桩,而无需锁定供应商。它旨在几乎不增加跟踪收集活动的开销,同时最大限度地提高跟踪工作的可移植性。
--微米追踪站点
要开始使用 Micrometer Tracing,需要添加一些依赖项:
org.springframework.boot:spring-boot-starter-actuator
io.micrometer:micrometer-tracing
io.micrometer:micrometer-tracing-bridge-otel
io.opentelemetry:opentelemetry-exporter-otlp
我们不需要 BOM,因为版本已经在 Spring Boot 父级中定义。
然而,我们需要两个运行时配置参数:跟踪应该发送到哪里,以及组件的名称是什么。它们由MANAGEMENT_OTLP_TRACING_ENDPOINT
和SPRING_APPLICATION_NAME
变量控制。
services: jaeger: image: jaegertracing/all-in-one:1.55 environment: - COLLECTOR_OTLP_ENABLED=true #1 ports: - "16686:16686" micrometer-tracing: build: dockerfile: Dockerfile-micrometer environment: MANAGEMENT_OTLP_TRACING_ENDPOINT: http://jaeger:4318/v1/traces #2 SPRING_APPLICATION_NAME: micrometer-tracing #3
结果如下:
无需任何定制,Micrometer 在接收和发送 HTTP 请求时会创建跨度。
框架需要为RestClient
注入魔力才能进行发送。我们必须让前者实例化后者:
@SpringBootApplication class MicrometerTracingApplication { @Bean fun restClient(builder: RestClient.Builder) = builder.baseUrl("http://localhost:8080/done").build() }
我们可以通过多种方式创建手动跨度,其中一种是通过 OpenTelemetry API 本身。但是,设置需要大量样板代码。最直接的方法是 Micrometer 的Observation API 。它的主要好处是使用一个 API 来管理指标和跟踪。
以下是更新后的代码:
class MicrometerController( private val restClient: RestClient, private val registry: ObservationRegistry ) { @GetMapping("/{message}") fun entry(@PathVariable message: String, @RequestHeader("X-done") done: String?) { logger.info("entry: $message") val observation = Observation.start("entry", registry) if (done == null) intermediate(observation) observation.stop() } fun intermediate(parent: Observation) { logger.info("intermediate") val observation = Observation.createNotStarted("intermediate", registry) .parentObservation(parent) .start() restClient.get() .header("X-done", "true") .retrieve() .toBodilessEntity() observation.stop() } }
添加的观察调用反映了生成的跟踪:
Micrometer Tracing 的替代方案是通用的OpenTelemetry Java Agent 。 它的主要优点是它既不影响代码也不影响开发人员;代理是一个纯粹的运行时范围关注点。
java -javaagent:opentelemetry-javaagent.jar agent-one-1.0-SNAPSHOT.jar
代理遵守 OpenTelemetry 的环境变量配置:
services: agent-1x: build: dockerfile: Dockerfile-agent1 environment: OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317 #1 OTEL_RESOURCE_ATTRIBUTES: service.name=agent-1x #2 OTEL_METRICS_EXPORTER: none #3 OTEL_LOGS_EXPORTER: none #4 ports: - "8081:8080"
/v1/traces
无需进行更多配置,我们得到以下跟踪:
代理会自动跟踪接收和发送的请求以及标有 Spring 相关注释的函数。根据调用堆栈,跟踪正确地嵌套在一起。要跟踪其他函数,我们需要在代码库中添加依赖项io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations
。现在,我们可以使用@WithSpan
注释注释以前未跟踪的函数。
value()
部分控制跟踪的标签,而kind
则转换为span.kind
属性。如果将值设置为空字符串(默认值),则会输出函数的名称。就我的目的而言,默认值就足够了。
@WithSpan fun intermediate() { logger.info("intermediate") RestClient.builder() .baseUrl("http://localhost:8080/done") .build() .get() .header("X-done", "true") .retrieve() .toBodilessEntity() }
它产生了预期的新的intermediate()
轨迹:
OpenTelemetry 于今年 1 月发布了该代理的新主要版本。我用它更新了我的演示;现在只有在应用程序接收和发送请求时才会创建跟踪。
与之前的版本一样,我们可以使用@WithSpan
注释添加跟踪。唯一的区别是我们还必须注释entry()
函数。默认情况下不会跟踪它。
Spring 之所以成功,有两个原因:它简化了复杂的解决方案,即EJB 2,并提供了一个超越竞争库的抽象层。Micrometer Tracing 最初是 Zipkin 和 Jaeger 上的抽象层,这完全是合理的。由于 OpenTelemetry 得到了大多数跨编程语言和跟踪收集器的库的支持,因此这一论点变得毫无意义。Observation API 仍然是 Micrometer Tracing 的一个相当大的优势,因为它使用了一个超越 Metrics 和 Traces 的 API。
在 Java 代理方面,OpenTelemetry 配置在所有技术堆栈和库中都是相似的 - 环境变量。当我从 v1 升级到 v2 时,我有点失望,因为新代理不支持 Spring:默认情况下不会跟踪带 Spring 注释的函数。
最后,这是一个明智的决定。明确说明您想要的跨度比删除一些您不想看到的跨度要好得多。
感谢Jonatan Ivanov的帮助和评论。
本文的完整源代码可以在 GitHub 上找到:
进一步来说:
最初于 2024 年 8 月 3 日在A Java Geek上发布