Spring Boot 微服务链路追踪实战:从 Sleuth 到 OpenTelemetry 的迁移方案与性能调优

链路追踪在微服务中的核心作用

微服务架构下,一个用户请求可能跨越 5-10 个服务,每个服务内部还有数据库、缓存、消息队列等多层调用。没有链路追踪,排查一次 P5 级别的延迟问题可能需要 3 个人花半天时间逐个服务翻日志。链路追踪让每次请求的完整调用链可视化,定位性能瓶颈和错误根因的效率提升一个数量级。

Spring Cloud Sleuth 在 3.x 后已停止维护,官方推荐迁移到 OpenTelemetry。OpenTelemetry 是 CNCF 的可观测性标准,统一了 traces、metrics、logs 三大信号,且与厂商解耦,数据可导出到 Jaeger、Zipkin、Tempo 等任意后端。

OpenTelemetry Agent 接入方式

Java 应用接入 OpenTelemetry 最简单的方式是使用 Agent 自动埋点,无需修改业务代码:

<!-- pom.xml 添加依赖 -->
<dependency>
    <groupId>io.opentelemetry.instrumentation</groupId>
    <artifactId>opentelemetry-spring-boot-starter</artifactId>
    <version>2.10.0-alpha</version>
</dependency>
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>

应用配置:

# application.yml
otel:
  exporter:
    otlp:
      endpoint: http://otel-collector:4317
      protocol: grpc
  resource:
    attributes:
      service.name: order-service
      service.version: 2.1.0
      deployment.environment: production
  traces:
    sampler:
      type: parentbased_traceidratio
      arg: 0.1   # 生产环境采样 10%
  instrumentation:
    spring-scheduling:
      enabled: true
    jdbc:
      enabled: true
    redis:
      enabled: true
    kafka:
      enabled: true

如果希望零侵入,使用 Java Agent 方式启动:

java -javaagent:opentelemetry-javaagent.jar \
  -Dotel.service.name=order-service \
  -Dotel.exporter.otlp.endpoint=http://otel-collector:4317 \
  -Dotel.traces.sampler=parentbased_traceidratio \
  -Dotel.traces.sampler.arg=0.1 \
  -jar app.jar

手动埋点与 Span 属性

自动埋点覆盖了 HTTP、RPC、数据库等标准框架,但业务关键节点的手动埋点同样重要:

@Service
@RequiredArgsConstructor
public class OrderService {

    private final Tracer tracer;

    public OrderResult createOrder(OrderRequest request) {
        // 创建子 Span 记录业务操作
        Span span = tracer.spanBuilder("order.create")
            .setAttribute("order.type", request.getType())
            .setAttribute("customer.id", request.getCustomerId())
            .setAttribute("order.items.count", request.getItems().size())
            .startSpan();

        try (Scope scope = span.makeCurrent()) {
            // 业务逻辑
            OrderResult result = processOrder(request);
            span.setAttribute("order.id", result.getOrderId());
            span.setAttribute("order.amount", result.getTotalAmount());
            return result;
        } catch (Exception e) {
            span.recordException(e);
            span.setStatus(StatusCode.ERROR, e.getMessage());
            throw e;
        } finally {
            span.end();
        }
    }
}

Span 属性是排查问题的关键线索。建议在以下位置添加属性:

  • 服务入口:请求来源、用户 ID、设备信息
  • 数据库操作:SQL 类型、影响行数、慢查询标记
  • 外部调用:目标服务名、响应状态码、耗时
  • 业务关键节点:订单号、金额、状态变更

OpenTelemetry Collector 部署架构

Collector 是数据的中间层,负责接收、处理、导出遥测数据。推荐部署模式:

# otel-collector-config.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  # 限流保护 Collector 自身
  rate_limiting:
    spans_per_second: 5000

  # 批量导出减少网络开销
  batch:
    send_batch_size: 1024
    timeout: 5s

  # 资源属性标准化
  resource:
    attributes:
      - key: env
        value: production
        action: upsert

  # 过滤掉健康检查的噪音 Span
  filter:
    error_mode: ignore
    traces:
      span:
        - 'http.route == "/health"'
        - 'http.route == "/ready"'

exporters:
  # 导出到 Jaeger
  otlp/jaeger:
    endpoint: jaeger:4317
    tls:
      insecure: true

  # 导出到 Prometheus(Metrics)
  prometheus:
    endpoint: 0.0.0.0:8889

  # 导出到日志文件
  file:
    path: /var/log/otel/traces.json

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [filter, rate_limiting, batch]
      exporters: [otlp/jaeger]
    metrics:
      receivers: [otlp]
      processors: [resource, batch]
      exporters: [prometheus]

采样策略与性能平衡

全量采集 Trace 的代价很高。一个日均千万级请求的系统,全量采集会产生数亿 Span,存储成本和查询性能都是问题。采样策略需要在可观测性和成本间找平衡:

# 基于规则的采样:错误请求全量采集,正常请求采样
# 在 Collector 中配置 tail_sampling
processors:
  tail_sampling:
    decision_wait: 10s
    num_traces: 100000
    expected_new_traces_per_sec: 1000
    policies:
      # 错误请求全量采集
      - name: error-policy
        type: status_code
        status_code:
          status_codes:
            - ERROR
      # 慢请求全量采集(>2s)
      - name: slow-policy
        type: latency
        latency:
          threshold_ms: 2000
      # 其他请求采样 5%
      - name: baseline-policy
        type: trace_id_ratio
        trace_id_ratio:
          sampling_percentage: 5

从 Sleuth 迁移注意事项

如果项目已经使用 Sleuth + Zipkin,迁移到 OpenTelemetry 需要处理几个兼容性问题:

1. Trace ID 格式变化

Sleuth 生成的 Trace ID 是 64 位十六进制(16 字符),OpenTelemetry 使用 128 位(32 字符)。跨系统调用时,ID 格式不匹配会导致链路断裂。解决方案是在 Collector 中做 ID 兼容处理,或统一所有服务迁移到 OpenTelemetry。

2. Baggage 传播

Sleuth 的 Baggage 通过 HTTP Header baggage 传播。OpenTelemetry 使用 W3C Baggage 规范,Header 名称一致但格式有细微差别。需要确认下游服务是否支持 W3C Baggage。

// 手动传播 Baggage
try (Scope scope = Baggage.builder()
    .put("user.id", userId)
    .put("tenant.id", tenantId)
    .build()
    .makeCurrent()) {
    // 此范围内的所有 Span 都能读取 Baggage
    downstreamClient.call(request);
}

3. 日志关联

Sleuth 自动在日志中注入 traceId 和 spanId。迁移到 OpenTelemetry 后,需要配置 Logback 或 Log4j2 的 MDC 注入:

<!-- logback-spring.xml -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId},%X{spanId}] %-5level %logger - %msg%n</pattern>

OpenTelemetry Agent 自动将 traceId 和 spanId 注入 MDC,无需额外配置。但需确认日志采集链路(Filebeat/Fluentd)能正确解析 MDC 字段,并在日志平台(如 ELK)中支持按 traceId 检索。

链路追踪是微服务可观测性的基石,从 Sleuth 迁移到 OpenTelemetry 虽然有一定工作量,但 OpenTelemetry 的统一标准和丰富生态是长期收益。建议分批迁移:先在新服务上使用 OpenTelemetry,老服务按迭代计划逐步切换,中间通过 Collector 做数据聚合。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot-wei-fu-wu-lian-lu-zhui-zong-shi-zhan-cong-sleuth/

(0)
小编小编
上一篇 6小时前
下一篇 6小时前

相关推荐