Spring Boot 3 :实现分布式追踪

向 Spring Boot 3 添加 OpenTelemetry 追踪的实用指南:代理设置、上下文传播、消息传递、采样和导出。

一个缓慢的结账请求。一个后台作业卡住等待另一个服务。一条看起来正常的日志消息------直到性能下降。在 Spring boot 微服务设置中,这些时刻会考验你的可观察性。你知道有些事情不对劲,但跨越数十个服务追踪请求感觉是不可能的。分布式追踪改变了这一点。它连接了请求旅程中的每个 span,准确地显示了时间花费在哪里以及问题开始出现的地方。

1 什么是现代微服务中的分布式追踪

在大多数现代系统中,一个单一请求在返回响应之前会经过多个服务、API 和数据库。如果没有清晰的方法来跟踪该路径,快速找到延迟或错误的来源很快就会变成猜测。分布式追踪通过跟踪每个请求跨服务的路径来解决这个问题,从而更容易发现延迟发生的位置以及导致延迟的原因。

分布式追踪是一种方法论和一组工具,旨在监控和可视化请求在分布式系统中流动的情况。它捕获请求的端到端旅程,提供其在多个服务、进程甚至不同基础设施组件中的执行路径的全面视图。这种整体视角对于解读复杂的交互至关重要。

① 为什么分布式追踪对 Spring Boot 3 应用程序至关重要

随着 Spring Boot 3 应用程序扩展到多个服务,跟踪它们之间发生的事情变得更加困难。一个单一请求可能在完成之前会通过 API 网关、身份验证层、业务服务和多个数据访问组件。如果没有分布式追踪,你只能猜测哪里出了问题。

调试速度减慢:很难查明哪个服务导致了延迟或返回了错误。

性能问题仍然隐藏:你无法轻易分辨哪个依赖项导致了延迟。

根本原因分析需要更长的时间:团队花费数小时关联跨系统的日志,从而增加了 MTTR(平均修复时间)。

对用户影响的可见性有限:你会忽略用户请求实际上是如何在你的应用程序中移动的。

分布式追踪为你提供了缺失的上下文。它将请求的每个步骤------跨服务、线程和 API------连接到一个单一的追踪中。你可以看到时间花费在哪里,哪些调用失败,以及一个缓慢的服务如何影响其余流程。

2 核心概念:追踪(Traces)、跨度(Spans)和上下文传播(Context Propagation)

在设置分布式追踪之前,有三个构建块使其能够工作:追踪、跨度和上下文传播。

① 追踪(Trace)

一个追踪是请求在您的系统中经过的完整路径。它显示了请求触及的每个服务------从您的 API 网关一直到数据库。您可以将其视为一个时间线,它捕获了从请求开始到完成之间发生的所有事情。

② 跨度(Span)

一个跨度是该请求中的一项工作。它可能是一个 API 调用、一个数据库查询或服务内部的函数执行。每个跨度都有一个名称、一个开始和结束时间,以及诸如状态代码或请求 URL 之类的属性。跨度是嵌套的------父跨度可能代表一个传入的 HTTP 请求,而其子跨度捕获由它触发的所有下游调用。

③ 上下文传播(Context Propagation)

这是追踪数据在服务之间移动的方式。当您的服务调用另一个服务时,它会传递追踪和跨度 ID------通常通过 HTTP 标头或消息元数据。这就是保持所有跨度连接为同一追踪的一部分的原因。如果没有适当的上下文传播,追踪会断裂,并且您会失去请求的端到端视图。

3 为 Spring Boot 3 选择您的分布式追踪方案

当您向 Spring Boot 3 应用程序添加分布式追踪时,您面临的首要决定是如何选择。正确的选择不仅决定了检测服务的难易程度,还决定了系统在未来如何扩展、集成和保持可维护性。

① OpenTelemetry:可观测性的未来

向 OpenTelemetry (OTel) 的转变标志着开发者处理可观测性的方式发生了重大演变。它不再仅仅是关于追踪------OTel 将指标、日志和追踪统一到一个一致的模型中。它在 CNCF 下构建,是开源且供应商中立的,使其成为需要灵活性和长期性的 Spring Boot 3 应用程序的理想选择。

以下是大多数现代 Spring Boot 团队转向 OTel 的原因:

供应商中立性:您可以在 Jaeger、Zipkin、Datadog 或 New Relic 等后端之间切换,而无需重写检测代码。

统一的可观测性:追踪、指标和日志遵循相同的标准------为您提供一个一致的真理来源。

语言一致性:如果您的生态系统混合了 Java、Go 和 Node.js,OTel 可以保持跨它们的遥测数据统一。

社区驱动的演进:在 Google、Microsoft 等公司的贡献下,OTel 发展迅速且保持稳定。

对于 Spring Boot 3 来说,OTel 不仅仅是一个选项------它是默认的方向。它用一个单一的、定义良好的标准取代了多种分散的追踪方法,该标准可以在团队和环境中扩展。

② 关于 Spring Cloud Sleuth ?

如果你使用 Spring 已经有一段时间了,Spring Cloud Sleuth 可能会让你感到熟悉。它曾经是从 Spring 应用程序中获取追踪信息的最简单方法------自动检测组件并与 Brave 和 Zipkin 完美配合。

但 Sleuth 已经走到了生命周期的尽头。它不再被积极维护,并且它的大部分功能现在已经合并到 OpenTelemetry 中。如果你仍然在使用 Sleuth,spring-cloud-sleuth-otel 桥提供了一个过渡路径------让现有的基于 Sleuth 的代码可以发出与 OpenTelemetry 兼容的追踪信息。

然而,对于新的 Spring Boot 3 项目,直接集成 OpenTelemetry 是更好的长期选择。它减少了一个额外的抽象层,让你对检测有更多的控制,并确保与 Spring 生态系统中未来的更新兼容。

③ 将 OpenTelemetry 集成到你的技术栈中

当然,采用新的追踪解决方案不会孤立发生------你会希望它与你现有的监控工具顺利协作。以下是一些有助于实现平稳过渡的考虑事项:

检查后端支持:大多数现代可观测性平台------从 Grafana Tempo 到 jaeger------都已经支持 OpenTelemetry 协议 (OTLP)。

避免数据孤岛:将追踪与指标和日志相关联是分布式追踪的闪光点。保持遥测数据的统一,以避免分析中的差距。

注意代理冲突:如果你的环境已经运行了 APM 代理,请确保它们不与 OpenTelemetry Java Agent 重叠,尤其是在字节码检测方面。

4 使用 Spring Boot 3 实现 OpenTelemetry 过程

有几种方法可以将 OpenTelemetry 添加到您的 Spring Boot 3 应用程序中------从使用 Java Agent 的即插即用设置到更灵活的、基于代码的配置。正确的方法取决于您对检测和数据流的控制程度。

① 使用 OpenTelemetry Java Agent

OpenTelemetry Java Agent 是启用追踪的最快方法。它不需要代码更改,并且会自动检测大多数常见框架------Spring MVC、WebFlux、JDBC、HTTP 客户端等等。

设置步骤:

从 OpenTelemetry Java Instrumentation releases 下载 agent。

主要配置:

java 复制代码
otel.service.name: 跟踪数据中服务的逻辑名称。
otel.exporter.otlp.endpoint:用于发送跟踪的收集器或后端端点。
otel.traces.exporter=otlp:设置 OTLP 导出器。
otel.resource.attributes=env=prod,app.version=1.0.0:将元数据添加到所有 span。

启动附加了 agent 的 Spring Boot 应用程序:

java 复制代码
java -javaagent:/path/to/opentelemetry-javaagent.jar \
     -Dotel.service.name=my-spring-boot-app \
     -Dotel.exporter.otlp.endpoint=http://localhost:4317 \
     -jar app.jar

代理会自动收集大部分的工具埋点,因此您无需更改代码即可查看跨控制器、存储库和下游 HTTP 调用的跟踪信息。

② 配置 OpenTelemetry SDK

对于高级设置,您可以直接在代码中定义 OpenTelemetry。这种方法更灵活------您可以自定义导出器、采样器和 span 处理器,以满足您的需求。

例子:

java 复制代码
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.semconv.ResourceAttributes;
public class OpenTelemetryConfig {
  public static OpenTelemetry initOpenTelemetry() {
    Resource serviceResource = Resource.getDefault()
      .merge(Resource.builder()
        .put(ResourceAttributes.SERVICE_NAME, "my-spring-boot-app")
        .put(ResourceAttributes.SERVICE_VERSION, "1.0.0")
        .put(ResourceAttributes.DEPLOYMENT_ENVIRONMENT, "dev")
        .build());
    OtlpGrpcSpanExporter otlpExporter = OtlpGrpcSpanExporter.builder()
      .setEndpoint("http://localhost:4317")
      .build();
    SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
      .addSpanProcessor(BatchSpanProcessor.builder(otlpExporter).build())
      .setResource(serviceResource)
      .build();
    OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder()
      .setTracerProvider(tracerProvider)
      .buildAndRegisterGlobal();
    Runtime.getRuntime().addShutdownHook(new Thread(tracerProvider::close));
    return openTelemetry;
  }
}

在 Spring Boot 中,您通常会将其公开为一个 @Configuration 类,并在需要的地方注入 OpenTelemetry 或 Tracer bean。

③ 使用 Micrometer Tracing 进行自动注入

Spring Boot 3 通过 Micrometer Tracing 包含对 OpenTelemetry 的一流支持。它可以自动捕获 Web 请求、数据库查询和消息传递的跟踪,而无需任何显式代理配置。 将这些依赖项添加到您的 pom.xml 或 build.gradle 中:

java 复制代码
<dependency>
  <groupId>io.micrometer</groupId>
  <artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
  <groupId>io.micrometer</groupId>
  <artifactId>micrometer-tracing-reporter-otlp</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

此设置使用 Micrometer 作为桥梁,并将跟踪发送到您配置的 OpenTelemetry 后端(通过 OTLP)。

④ 用于自定义逻辑的手动检测

即使使用自动跟踪,有时您也需要手动 span 来捕获自定义工作流程或业务逻辑。 例子:

java 复制代码
@Service
public class ProductService {
  private final Tracer tracer;
  public ProductService(Tracer tracer) {
    this.tracer = tracer;
  }
  public Product getProductDetails(String productId) {
    Span span = tracer.spanBuilder("getProductDetails")
        .setAttribute("product.id", productId)
        .startSpan();
    try {
      // Simulate work
      Thread.sleep(50);
      return new Product(productId, "Example Product", 99.99);
    } catch (Exception e) {
      span.recordException(e);
      span.setStatus(io.opentelemetry.api.trace.StatusCode.ERROR, "Failed to fetch product details");
      throw e;
    } finally {
      span.end();
    }
  }
}

当您想要追踪特定领域的运营时,手动 Span 非常有用,例如 checkout.process、payment.validate 或 user.signup,这些是自动检测无法推断的。

每种方法------基于 Agent、SDK、Micrometer 或手动------都服务于不同的需求。首先使用自动检测快速获得全面覆盖,并随着您的追踪需求成熟,逐步添加手动 Span 或基于 SDK 的配置。

5 在服务间传播追踪上下文

一旦追踪在服务之间中断,它的价值就会丧失。如果追踪上下文没有传递下去,最终你会得到碎片化的 span,只能讲述部分故事。在服务、线程和队列之间维护上下文,可确保每个请求都可以进行端到端的追踪。

① W3C 追踪上下文和 B3 传播

OpenTelemetry 遵循开放标准,以维护跨服务边界的上下文。最常见的是 W3C 追踪上下文,它定义了两个关键的 header:

traceparent -- 携带追踪 ID、父 span ID 和追踪标志。

tracestate -- 包含供应商特定或自定义的追踪数据。

OpenTelemetry 还支持 B3 传播,Zipkin 使用的格式,它依赖于诸如 X-B3-TraceId、X-B3-SpanId 和 X-B3-Sampled 之类的 header。

无论你选择哪种格式,一致性都很重要。系统中的所有服务都应使用相同的传播标准。对于 Spring Boot 3,OpenTelemetry 默认使用 W3C 追踪上下文------这是未来推荐的选择。

② 确保上下文在 REST API 中流动

在基于 REST 的系统中,OpenTelemetry 会自动处理你的追踪上下文。

传入请求:当你的 Spring Boot 服务收到 HTTP 调用时,OpenTelemetry 会提取 traceparent 和 tracestate header,并将该数据附加到当前的 span。

传出请求:当你的服务使用 RestTemplate 或 WebClient 发出出站调用时,OpenTelemetry 会将相同的追踪上下文注入到传出的请求 header 中。

这可以使你的追踪在多个服务之间保持连续,而无需手动设置。

③ 在异步和基于消息的系统中处理上下文

异步操作和消息队列需要额外注意,因为 HTTP header 不直接适用。 OpenTelemetry 提供了在这些场景中传播上下文的方法。

@Async 方法:当你使用 Spring 的 @Async 时,OpenTelemetry 集成会自动在同一 JVM 内携带当前的追踪上下文。

消息队列(Kafka、RabbitMQ):对于服务间的消息传递,在发布之前将追踪上下文注入到消息 header 中,并在消费时提取它。

发送消息示例 (Kafka):

java 复制代码
Span currentSpan = Span.current();
Context context = Context.current().with(currentSpan);
OpenTelemetry.getGlobalPropagators().getTextMapPropagator()
  .inject(context, recordHeaders,
    (headers, key, value) -> headers.add(key, value.getBytes()));

接收消息:

java 复制代码
Context extracted = OpenTelemetry.getGlobalPropagators().getTextMapPropagator()
  .extract(Context.current(), headers,
    (hdrs, key) -> {
      Header header = hdrs.lastHeader(key);
      return header != null ? new String(header.value()) : null;
    });
Tracer tracer = OpenTelemetry.getGlobalOpenTelemetry().getTracer("consumer-service");
Span consumerSpan = tracer.spanBuilder("processMessage")
  .setParent(extracted)
  .setSpanKind(SpanKind.CONSUMER)
  .startSpan();
try (Scope scope = consumerSpan.makeCurrent()) {
  // message processing logic
} finally {
  consumerSpan.end();
}

合适的传播确保您的追踪保持连续性------从 HTTP 调用到异步作业和消息队列------让您能够全面、准确地了解分布式系统的行为。

6 将 OpenTelemetry 与 Spring Boot 3 组件集成

OpenTelemetry 的真正优势在于它能够连接 Spring Boot 3 应用程序的不同部分------数据库、消息代理和 Web 客户端。每一层都有其自身的可见性,形成一个连续的画面,展示请求如何在您的系统中传递。

① 数据库交互(JPA、JDBC)

OpenTelemetry Java Agent 会自动检测大多数标准 JDBC 驱动程序。每次您的应用程序运行查询或更新时,都会为该数据库调用创建一个新的 span。

每个 span 都包含以下属性:

db.system:数据库类型(例如,mysql、postgresql)。

db.connection_string:连接详情(已清理)。

db.statement:SQL 查询文本(确保敏感数据已屏蔽)。

db.user:数据库用户名。

net.peer.name / net.peer.port:主机和端口信息。

对于 JPA 和 Hibernate,OpenTelemetry 会钩入底层 JDBC 调用,因此您无需额外设置即可获得相同的可见性。这有助于及早发现慢查询、低效的连接或 N+1 问题。

② 消息代理(Kafka、RabbitMQ)

消息驱动的系统严重依赖于上下文传播,而 OpenTelemetry 会自动处理最流行的代理。

Kafka:Java Agent 会检测 kafka-clients 库。它将追踪上下文注入到 ProducerRecord 标头中,并从 ConsumerRecord 标头中提取它------创建连接消息发送和接收操作的 PRODUCER 和 CONSUMER span。

RabbitMQ:类似的检测可用于 amqp-client,通过消息属性传播追踪上下文。

这种自动处理使您可以追踪整个消息生命周期------从生产者到消费者------而无需修改业务逻辑。

③ Web 客户端(RestTemplate、WebClient)

Spring Boot 服务通常通过 HTTP 调用其他服务。OpenTelemetry 默认检测 RestTemplate 和 WebClient。

RestTemplate:当您的应用程序发出 HTTP 请求时,OpenTelemetry 会将追踪上下文标头注入到传出的调用中,确保下一个服务可以继续追踪。

WebClient:在反应式应用程序中,OpenTelemetry 通过反应式链传播上下文,即使在异步流中也能保持追踪的连续性。

通过此设置,服务间的调用(同步或反应式)都将显示为同一追踪的一部分。

④ Spring Security 上下文集成

OpenTelemetry 专注于请求和依赖关系追踪,但您可以使用 Spring Security 中的用户上下文来丰富 span。

例如,您可以将经过身份验证的用户信息(如用户 ID 或角色)添加到 span 中,以获得更好的调试和审计可见性。

java 复制代码
import io.opentelemetry.api.trace.Span;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
@Component
public class UserTracingInterceptor {
  public void addUserDetailsToCurrentSpan() {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    if (auth != null && auth.isAuthenticated()) {
      Span.current().setAttribute("user.name", auth.getName());
      // Optionally add roles or user.id
    }
  }
}

你可以根据你的安全流程,从拦截器、过滤器或AOP切面调用它。添加安全上下文有助于追踪特定用户如何与系统交互------这对于分析用户影响或调试与访问相关的问题非常有用。

7 将追踪数据导出到可观测性后端

一旦你的 Spring Boot 应用程序开始生成追踪数据,下一步就是将这些数据导出到可观测性后端,以便进行存储、查询和可视化。OpenTelemetry 生态系统支持多种导出选项,但大多数现代系统依赖 OpenTelemetry 协议 (OTLP) 来实现一致且高效的数据传输。

① 配置适用于 Jaeger、Zipkin 或 Prometheus 的 OTLP 导出器

OpenTelemetry 协议 (OTLP) 是将遥测数据(包括追踪、指标和日志)发送到外部系统的标准。它可以通过 gRPC(默认端口 4317)或 HTTP(默认端口 4318)工作,从而在各种环境中提供灵活性。

常见后端:

Jaeger:运行一个暴露 OTLP 端点的 Jaeger Collector。OpenTelemetry Java Agent 或 SDK 可以将追踪数据直接发送到此 Collector。Jaeger 的 UI 提供了跨服务的 Span 的详细可视化和筛选。

Zipkin:Zipkin 也可以通过其 Collector 接收 OTLP 数据。它很轻量级,非常适合较小或本地的追踪设置。

Prometheus / Tempo:Prometheus 主要用于指标,但你可以将其与 Grafana Tempo 搭配使用来存储和可视化追踪数据。OpenTelemetry 可以通过 Prometheus 导出器导出指标,并通过 OTLP 将追踪数据导出到 Tempo 或其他追踪后端。

示例配置 (Java Agent):

java 复制代码
# OTLP over gRPC
otel.exporter.otlp.endpoint=http://localhost:4317
otel.traces.exporter=otlp
# OTLP over HTTP
otel.exporter.otlp.traces.endpoint=http://localhost:4318/v1/traces
otel.traces.exporter=otlp

② 保障数据安全与隐私

链路数据通常包含可能暴露敏感信息的上下文。保护这些信息应该是链路追踪设置的一部分。

掩蔽敏感数据:避免在 Span 属性中记录个人身份信息、凭据或机密负载。使用自定义的清理逻辑或过滤器来移除或编辑这些字段。

控制访问:将链路存储访问权限限制于授权用户。在您的可观测性后端配置基于角色的访问控制。

传输中加密:始终对所有 OTLP 端点使用 HTTPS 或 TLS,以保护从应用程序到后端传输的链路数据安全。

定义保留策略:为链路数据设置明确的保留期限。仅保留用于故障排除和合规性所需的数据。

8 最佳实践

为了使您的链路追踪数据有用,您需要遵循一些关键实践,以保持其一致性、有意义和高效。

① Span 的一致命名约定

良好的 Span 名称使链路更容易阅读和理解。

描述性:使用清晰描述 Span 代表什么的名字,例如 UserService.getUserById、OrderRepository.saveOrder 或 processKafkaMessage。

避免高基数:不要在 Span 名称中包含唯一值,例如 ID 或时间戳。而是使用属性。

遵循语义约定:坚持 OpenTelemetry 的命名模式,例如 HTTP GET /users/{id} 或 SELECT FROM users。这些约定使 Span 统一,更容易聚合或查询。

一致的命名可以帮助您快速发现请求的哪个部分速度慢或出现故障,而无需猜测 Span 代表什么。

② 添加相关的属性以提供上下文

属性通过元数据丰富 Span,从而提供技术和业务上下文。

业务上下文:包含 customer.idorder.idtenant.id 等值,以将技术事件与用户影响联系起来。

技术上下文:捕获诸如 db.system、http.status_code 或 message.queue_name 等详细信息。

错误详细信息:发生故障时添加 error.type、error.message 或 exception.stacktrace。

资源属性:保持所有 Span 中的 service.name、service.version 和 host.name 一致。

精心选择的属性可以将原始链路转化为您在事件分析期间实际可以使用的东西。

③ 策略性地管理采样

在高吞吐量环境中,收集每个链路可能会使您的后端不堪重负。采样使您可以控制数据量,同时仍然捕获最有价值的链路。

头部采样:在请求开始时做出决定。如果第一个 Span 被采样,则收集所有子 Span。

基于父级的采样:从父 Span 继承决策,但可以在本地覆盖。

基于链路 ID 比率的采样:捕获固定百分比的链路(例如 1-5%)。

尾部采样:在链路完成后(通常在 OpenTelemetry Collector 中)根据错误或延迟等结果做出决定。

对于大多数 Spring Boot 3 服务,ParentBasedSampler 结合低比率效果很好。您可以始终对所有错误链路进行采样,以确保捕获故障。

④ 使用链路数据进行监控和警报

链路不仅仅用于调试;它们是丰富的运营信号来源。

错误率:当太多链路包含错误 Span 时发出警报。

延迟异常:跟踪关键端点的响应时间,并在超过正常阈值时触发警报。

SLO:根据链路数据定义性能目标(例如,结账请求的第 99 个百分位延迟)。

服务依赖关系:使用链路图及早识别发生故障的下游服务。

当链路为您的监控提供支持时,您可以在减速或依赖关系故障到达用户之前检测到它们。

⑤ 最大限度地减少性能开销

链路追踪不可避免地会增加一些成本,但可以有效地管理它。

CPU 和内存:OpenTelemetry Java Agent 是轻量级的,但复杂的手动 Span 可能会增加开销。

网络 I/O:使用批处理和 OTLP gRPC 来减少导出流量。

采样:控制成本的最简单有效的方法。

资源清理:始终在关闭时关闭 TracerProvider 或 SDK 资源,以防止泄漏。

在启用链路追踪的情况下监控您的系统,并根据实际性能调整您的采样率或检测深度。

应用这些实践可确保 Spring Boot 3 环境中的链路追踪提供有用、可操作的数据,而不会产生噪音或不必要的负载。

9 排查常见分布式追踪问题

Spring Boot 中的分布式追踪可能会因特定且可诊断的原因而失败。以下检查涵盖了缺失 span、上下文中断和追踪开销的最常见技术原因。

① 缺失 Span 或不完整追踪

如果请求的某些部分在您的后端不可见:

Agent 未加载:使用 -javaagent:/path/to/opentelemetry-javaagent.jar 运行应用程序。检查启动日志 --- 如果您没有看到 otel.javaagent,则表示它未附加。

不支持的库:确认您的依赖项出现在 OpenTelemetry Java Instrumentation 支持的库列表中。对于不支持的框架,请使用通过 Tracer.spanBuilder() 的手动instrumentation。

异步边界:对于 @Async 方法或消息消费者,使用 Context.current().wrap() 包装任务,或使用 Context.taskWrappingExecutor() 配置 ExecutorService。

采样率过低:在 otel-config.properties 中,验证 otel.traces.sampler=parentbased_traceidratio 和 otel.traces.sampler.arg=0.05(对于 5% 采样)。

Exporter 未连接:检查 agent 日志中是否存在类似 Failed to export spans 的行。测试 OTLP collector 的可达性:

java 复制代码
curl -v http://localhost:4318/v1/traces

上下文丢失:如果 span 启动了新的追踪 ID,而不是延续相同的追踪,请检查请求标头。确保所有入站和出站请求都存在 traceparent 和 tracestate 标头。使用 curl 的示例检查:

java 复制代码
curl -v http://service-b:8080/api --header "traceparent: 00-<trace-id>-<span-id>-01"

② 上下文传播失败

链路连续性取决于跨服务和线程上下文的一致交换。

跨语言调用:验证传播格式。如果存在混合环境,请显式设置它:

java 复制代码
otel.propagators=tracecontext,baggage

Kafka/RabbitMQ:确保生产者和消费者使用相同的传播器(W3C 或 B3)。Kafka 消息头的示例如下:

clike 复制代码
OpenTelemetry.getGlobalPropagators().getTextMapPropagator()
  .inject(Context.current(), record.headers(),
    (headers, key, value) -> headers.add(key, value.getBytes(StandardCharsets.UTF_8)));

线程池:包装 runnable 以维护 Context:

clike 复制代码
executor.submit(Context.current().wrap(() -> doWork()));

自定义 HTTP 客户端:手动注入和提取 header:

clike 复制代码
//手动注入
propagator.inject(Context.current(), request, (req, key, value) -> req.addHeader(key, value));
//提取 header
Context extracted = propagator.extract(Context.current(), response, (resp, key) -> resp.getHeader(key));

③ 与追踪相关的性能下降

追踪会引入可衡量的开销。使用特定指标和配置进行监控和调整。

CPU 使用率:使用 otel.javaagent.debug=true 分析 span 创建成本。在日志中查找 SpanProcessor.onStart 时间。

Exporter 队列延迟:检查 collector 队列大小 (otel.exporter.otlp.metrics.default.histogram.aggregation.temporality) 和 exporter 线程池指标。

高基数属性:避免像 user.id 或 request.uuid 这样的标签。如果需要,在附加之前对值进行哈希或规范化。

GC 压力:过多的短生命周期 span 会增加 GC 时间。使用异步 span 处理器和批量导出器以最大限度地减少分配 churn。

尾部采样:如果使用基于 collector 的尾部采样,请确保规则不会过滤过于激进。对错误或延迟属性进行采样:

clike 复制代码
processors:
  tail_sampling:
    policies:
      - name: error-traces
        type: status_code
        status_code: ERROR

批处理:调整批处理大小以控制吞吐量:

clike 复制代码
otel.bsp.schedule.delay=5000
otel.bsp.max.queue.size=2048
otel.bsp.max.export.batch.size=512

以下是一个可以执行的快速检查清单:

确认代理已附加,并且日志包含"OpenTelemetry Java Agent started."。

验证所有 HTTP 请求中都存在 traceparent 标头。

使用 -Dotel.logs.level=debug 运行以检查导出器输出。

验证 OTLP 收集器的可达性和后端接收。

使用 ParentBasedSampler,并设置一个比率,该比率可以在不饱和存储的情况下捕获关键跟踪。

相关推荐
初次攀爬者9 小时前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺9 小时前
搞懂@Autowired 与@Resuorce
java·spring boot·后端
Derek_Smart10 小时前
从一次 OOM 事故说起:打造生产级的 JVM 健康检查组件
java·jvm·spring boot
Nyarlathotep011316 小时前
SpringBoot Starter的用法以及原理
java·spring boot
dkbnull1 天前
深入理解Spring两大特性:IoC和AOP
spring boot
洋洋技术笔记2 天前
Spring Boot条件注解详解
java·spring boot
洋洋技术笔记3 天前
Spring Boot配置管理最佳实践
spring boot
用户8307196840823 天前
Spring Boot 项目中日期处理的最佳实践
java·spring boot
初次攀爬者3 天前
ZooKeeper 实现分布式锁的两种方式
分布式·后端·zookeeper
大道至简Edward4 天前
Spring Boot 2.7 + JDK 8 升级到 Spring Boot 3.x + JDK 17 完整指南
spring boot·后端