OpenTelemetry 入门:架构、组件与 SDK 安装

讲链路追踪,绕不开 OpenTelemetry。你现在出去面试,聊可观测性,十个有八个会问你用过 OTel 没有。它已经是事实上的标准了。这章我们就把它的底裤扒开,看看里面到底长什么样。

为什么需要 OpenTelemetry?

我之前在一个老项目里,接了三种不同的 APM 探针。Jaeger 的、Zipkin 的,还有一个自研的。每次要查个全链路,得在三个系统里反复横跳,数据格式还不一样,对接得想死。说白了,接口协议都不统一,更别提什么生态了。

OpenTelemetry 做了什么事呢?它统一了数据采集的协议。不管你是 Jaeger、Zipkin 还是 Prometheus,都通过 OTel 这把“瑞士军刀”来采集。数据格式统一了,后面的存储和展示选择就自由了。

OpenTelemetry 的整体架构

我们先看张图,脑袋里有个整体印象。

OpenTelemetry 核心架构 应用程序 (携带 OTel SDK) Exporter OTel Collector Receiver | Processor Exporter | Pipeline Jaeger 分布式追踪存储 Prometheus 指标监控系统 Loki / 日志 日志分析平台 📌 数据流转路径 应用内:通过 SDK 生成 Span/Trace → Exporter 初步发送 中心化:OTel Collector 接收 → 处理(过滤/采样/增强)→ 分发到后端系统

这图里其实就三块核心:SDK、Collector 和 后端系统。我们逐个说。

SDK:嵌入应用的探针

SDK 是你需要打到代码里的库。它负责拦截你程序的 HTTP 调用、数据库查询、消息队列等等,生成一个个 Span,再把 Span 串成 Trace。嗯,这里要注意:SDK 只负责“生成”和“发射”,它不负责永久存储。

SDK 内部的核心组件是 Exporter。它决定了数据发往哪里去。我们做个对比:

组件职责我的建议/经验
SDK 核心自动/手动埋点,生成 Span优先用自动埋点,省力。但核心链路建议手动增加业务标签
Processor(SDK 侧)本地的批处理、过滤、采样我常用尾巴采样(Tail Sampling),只保留慢请求的 Trace
Exporter(SDK 侧)将数据发送到 Collector 或后端推荐用 gRPC 协议,吞吐量比 HTTP 高。我之前踩过 HTTP 的坑,数据量一大就超时

Collector:数据中转与加工站

Collector 是 OTel 生态里我最喜欢的部分。它像一个中心化的数据管道。你想想看,如果每个微服务都直接把数据发到 Jaeger,改配置要改几十个服务,多痛苦。有了 Collector,你只需要改 Collector 的配置就行了。

Collector 组件也分三个:

  • Receiver:负责接收来自 SDK 或者其他系统(比如 Prometheus)的数据。支持 OTLP、Jaeger、Zipkin 等协议。
  • Processor:数据处理工厂。可以过滤掉健康检查的垃圾 Span、可以采样(比如每秒只保留前 100 条 Trace)、可以给数据加业务标签。
  • Exporter:将处理完的数据送往目标。可以同时写往多个后端,比如一份写 Jaeger 存储,一份写日志平台归档。
⚠️ 我曾经在这里翻过车:Collector 的 Processor 是有顺序的。比如你先加了过滤,然后又加了一个增加标签的 Processor。如果过滤把数据全丢了,后面的标签加给谁呢?顺序很重要,建议按“过滤 → 采样 → 增强 → 导出”来配。

Processor 的常见用途

结合实际项目,我推荐你关注这几个 Processor:

  1. Batch Processor:必配。将多个 Span 打包成一批发送,减少网络开销。我在高并发的服务上试过,不配 Batch,Collector 的 CPU 直接飙到 80%,配上后降到 20% 以下。
  2. Tail Sampling Processor:强推。全量采集太费钱,这个可以让你只保留那些“慢的”或者“出错的”请求。比如 P99 以上的请求保留,其他丢弃,成本降低十倍。
  3. Attributes Processor:给 Span 加常见属性。比如从请求 Header 里提取用户的租户 ID 加到 Span 上,后面查问题直接按租户筛选。

SDK 安装:以 Java 为例

纸上谈兵没意思,我们直接上手。底层逻辑都一样,换个语言就是换个包名。

// 1. 引入依赖(Maven)
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-api</artifactId>
    <version>1.31.0</version>
</dependency>
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-sdk</artifactId>
    <version>1.31.0</version>
</dependency>
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-otlp</artifactId>
    <version>1.31.0</version>
</dependency>

// 2. 初始化 SDK(往往在应用启动时执行)
OpenTelemetrySdk sdk = OpenTelemetrySdk.builder()
        .setTracerProvider(
            SdkTracerProvider.builder()
                .addSpanProcessor(BatchSpanProcessor.builder(
                    OtlpGrpcSpanExporter.builder()
                        .setEndpoint("http://otel-collector:4317") // 指向 Collector
                        .build()
                ).build())
                .build()
        )
        .build();

// 3. 全局设置,方便各处调用
OpenTelemetry.setGlobalOpenTelemetry(sdk);

你看,核心就几步:
1. 引入包
2. 创建 Exporter,指向 Collector
3. 配置 Processor(这里是 Batch 模式)
4. 全局注册,完事。

💡 我的实践经验:很多人在配置 Exporter 的 Endpoint 时,容易写错协议。Collector 的 gRPC 端口默认是 4317,HTTP 是 4318。如果你用 OtlpGrpcSpanExporter,地址得是 4317 端口。我当年第一次配时,把 HTTP 地址填到 gRPC Exporter 里,查了一天日志才找到问题,气死。

避坑清单(实战总结)

最后给你列个清单,都是我曾经在线上环境摔过的跟头:

  • 版本一致性:API、SDK、Exporter、Collector 的版本号尽量保持一致。大版本不匹配,可能会序列化失败。
  • 采样率先低后高:刚上线时,别开 100% 采样。量大的服务,全量采样能把后端写挂。我习惯先开 5% 看看效果,稳定了再调整。
  • Collector 的高可用:别单点部署 Collector,至少两台。万一挂了一台,SDK 会重连到另一台。我们在 Kubernetes 上通常用 StatefulSet 部署,配 Headless Service 做负载均衡。
  • 小心敏感数据:Span 的 Attributes 里别塞用户密码、身份证号等隐私字段。如果必须加,记得在 Processor 里用 Filter 或 Mask 处理掉。