4. OpenTelemetry 与 Jaeger:OTel 协议、SDK 接入、Exporter 配置

在事故响应场景中,快速定位故障根因依赖于高质量、低延迟的链路数据。OpenTelemetry(OTel)已成为分布式追踪的事实标准,而 Jaeger 作为原生支持 OTel 的后端,二者结合构成了现代可观测性基础设施的核心。本节将深入剖析 OTel 协议、SDK 接入方式以及 Exporter 配置,帮助你在变更窗口内快速部署并获取有效追踪数据。

4.1 OTel 协议:数据模型与传输规范

OpenTelemetry 协议(OTLP)定义了链路、指标、日志的统一数据模型和传输格式。理解 OTLP 是正确配置 Jaeger 的基础。

协议层面 说明 Jaeger 兼容性
gRPC OTLP 基于 Protobuf 的高性能二进制传输,推荐生产环境使用 Jaeger v1.35+ 原生支持 gRPC 接收器
HTTP/JSON OTLP 基于 HTTP 的 JSON 编码,调试和受限环境友好 Jaeger 通过 otlp-http 接收器支持
Jaeger Thrift Jaeger 原生旧协议,即将被 OTLP 取代 向后兼容,但建议迁移至 OTLP
事故响应建议: 在变更窗口内,优先使用 gRPC OTLP 协议。其二进制编码和流式传输特性可减少网络开销,避免因链路数据堆积导致追踪系统自身成为瓶颈。

4.2 OTel SDK 接入:从代码到链路

SDK 接入是生成有效追踪数据的起点。以下以 Java 为例,展示如何在微服务中集成 OTel SDK 并导出至 Jaeger。

4.2.1 依赖引入与自动配置

<!-- pom.xml -->
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-api</artifactId>
    <version>1.40.0</version>
</dependency>
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-sdk</artifactId>
    <version>1.40.0</version>
</dependency>
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-otlp</artifactId>
    <version>1.40.0</version>
</dependency>

4.2.2 手动创建 Span 示例

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;

public class OrderService {
    private final Tracer tracer;

    public OrderService(OpenTelemetry openTelemetry) {
        this.tracer = openTelemetry.getTracer("order-service");
    }

    public void processOrder(String orderId) {
        Span span = tracer.spanBuilder("processOrder")
                .setAttribute("order.id", orderId)
                .setAttribute("service.version", "1.2.3")
                .startSpan();
        try (Scope scope = span.makeCurrent()) {
            // 业务逻辑:数据库查询、外部调用等
            Thread.sleep(100); // 模拟耗时
            span.addEvent("order.processed", Attributes.of(
                    AttributeKey.stringKey("result"), "success"));
        } catch (Exception e) {
            span.recordException(e);
            span.setStatus(StatusCode.ERROR, "订单处理失败");
        } finally {
            span.end();
        }
    }
}
常见陷阱: 在事故响应中,不要忘记在 finally 块中调用 span.end()。未关闭的 Span 会导致内存泄漏和链路断裂,直接影响根因定位的准确性。

4.3 Exporter 配置:将数据送达 Jaeger

Exporter 负责将 SDK 采集的 Span 数据发送到 Jaeger 后端。配置错误是变更窗口内追踪数据丢失的首要原因。

4.3.1 环境变量配置(推荐生产使用)

# 设置 OTLP gRPC 导出器
OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger-collector:4317
OTEL_EXPORTER_OTLP_PROTOCOL=grpc
OTEL_SERVICE_NAME=order-service
OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production,service.version=1.2.3

# 采样配置(事故响应时建议全量采样)
OTEL_TRACES_SAMPLER=always_on

4.3.2 Java 代码级配置

import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;

public class TelemetryConfig {
    public static OpenTelemetry initJaegerExporter() {
        OtlpGrpcSpanExporter exporter = OtlpGrpcSpanExporter.builder()
                .setEndpoint("http://jaeger-collector:4317")
                .setTimeout(10, java.util.concurrent.TimeUnit.SECONDS)
                .build();

        SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
                .addSpanProcessor(BatchSpanProcessor.builder(exporter)
                        .setMaxExportBatchSize(512)
                        .setExporterTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
                        .build())
                .build();

        return OpenTelemetrySdk.builder()
                .setTracerProvider(tracerProvider)
                .build();
    }
}

4.4 架构总览:从 SDK 到 Jaeger 的完整链路

下图展示了 OTel SDK 采集、Exporter 传输、Jaeger 后端存储与查询的完整数据流。

OTel 到 Jaeger 数据流架构图 OTel SDK → Jaeger 数据流架构 微服务 A OTel SDK 微服务 B OTel SDK 微服务 C OTel SDK OTLP Exporter gRPC / HTTP BatchSpanProcessor Jaeger 后端 Collector (OTLP 接收) Storage (Elasticsearch/Cassandra) Query Service Jaeger UI 关键说明: SDK 通过 BatchSpanProcessor 批量导出 Span → OTLP Exporter 使用 gRPC 协议发送 → Jaeger Collector 接收并存储 → Query Service 提供 UI 查询。

4.5 配置清单:变更窗口快速验证

在事故响应变更窗口内,使用以下清单快速验证 OTel 到 Jaeger 的连通性:

  1. 检查 Exporter 端点:确认 OTEL_EXPORTER_OTLP_ENDPOINT 指向正确的 Jaeger Collector 地址和端口(默认 4317)。
  2. 验证协议一致性:SDK 端使用 gRPC 时,Jaeger Collector 必须启用 gRPC 接收器(--collector.otlp.enabled=true)。
  3. 确认采样策略:事故定位期间设置 OTEL_TRACES_SAMPLER=always_on,避免因采样丢失关键链路。
  4. 检查网络连通性:使用 telnet jaeger-collector 4317 或 grpcurl 测试端口可达性。
  5. 查看 SDK 日志:启用 OTel SDK 的调试日志(OTEL_LOG_LEVEL=debug),确认无导出错误。
  6. Jaeger UI 验证:在 Jaeger UI 中搜索服务名,确认有新的 Trace 数据到达。
事故响应最佳实践: 在变更窗口开启前,预先部署一个“健康检查”微服务,持续发送测试 Span 到 Jaeger。一旦变更开始,通过对比测试 Span 的到达延迟和完整性,可以快速判断 OTel 配置是否被变更破坏。

4.6 常见问题与排查

问题现象 可能原因 排查步骤
Jaeger UI 无数据 Exporter 端点配置错误或网络不通 检查 OTEL_EXPORTER_OTLP_ENDPOINT;使用 curl 测试 Collector 健康端点
Span 丢失或延迟高 BatchSpanProcessor 参数不合理 增大 setMaxExportBatchSize 和 setExporterTimeout;检查网络带宽
协议不兼容错误 SDK 使用 gRPC,Collector 未启用 确认 Jaeger Collector 启动参数包含 --collector.otlp.enabled=true
采样率过低 默认采样策略为 parentbased_always_off 临时设置为 always_on 或调整采样率至 100%

通过掌握 OTel 协议、SDK 接入和 Exporter 配置,你可以在事故响应变更窗口内快速建立可靠的分布式追踪管道,为后续的根因定位提供坚实的数据基础。