3. OpenTelemetry 标准:架构、SDK 与 Exporter

说到全链路监控,OpenTelemetry 已经是事实标准了。我记得早些年,大家还在为 Jaeger 还是 Zipkin 的选型吵得不可开交,埋点代码也经常绑死在某一个厂商的 SDK 上。后来 CNCF 把 OpenTracing 和 OpenCensus 合并成 OpenTelemetry,业内才算真正统一了思路。

这节课,我带大家把 OpenTelemetry 的骨架拆开看看。说白了,我们只需要搞懂三件事:数据怎么产生的、怎么处理的、怎么送出去的。 对应到组件上,就是 SDK、Collector 和 Exporter。

3.1 OpenTelemetry 的整体架构

先看一张我手绘的架构图,帮你建立全局印象:

OpenTelemetry 核心架构流程 OpenTelemetry 核心组件 & 数据流 应用程序 / 服务 线上业务代码 自动埋点 / 手动埋点 OpenTelemetry SDK SpanProcessor Sampler Context 传播 Exporter 导出器 OTLP (gRPC/HTTP) Jaeger / Zipkin Prometheus / 日志 Collector 接收器 处理器 导出器 💡 数据流动方向 应用代码 → SDK 采集(Span / Metric / Log)→ Exporter 发送 → Collector 接收处理 → 最终存储 Collector 是可选的,小规模可以直接 Exporter 到后端,但生产环境我建议一定加上 Collector。

这张图展示的,就是一条 Trace 从出生到归档的完整路径。我习惯把 OpenTelemetry 分成三层来看:SDK 层负责采集,Exporter 负责传输,Collector 负责收口。 咱们从最基础的部分往外聊。

3.2 SDK:埋点的灵魂

SDK 是跑在应用进程里的。它决定了你的 Span、Metric 和 Log 能不能被正确地创建出来。我第一次接触 OTel SDK 时,最怕的就是上下文传播出了问题,导致一条 Trace 断成了好几截。

SDK 里核心的组件其实就三个:

  • Provider: 全局的工厂,用来创建 Tracer、Meter 这些实例。你在代码开头配置一次就行。
  • SpanProcessor: Span 的处理器。它可以对 Span 做过滤、增强、批量打包。生产环境一般用 BatchSpanProcessor,不会每来一个 Span 就发送一次。
  • Sampler: 采样器。我个人经验是,线上流量大的系统,一定要设置采样率。要不然扛不住的。可以用 ParentBasedSampler 配合固定比例采样。

小提示: 我建议你在开发环境把采样率设成 1.0,但预发布环境立刻调低到 0.1 甚至 0.01。我曾经就因为忘记改采样率,把测试环境的 InfluxDB 直接打爆过——那真是血的教训。

3.2.1 自动埋点 vs 手动埋点

SDK 落地的时候,会面临这个选择。很多人问我:"老师,我能不能全靠自动埋点?" 我的回答是:可以,但不建议全依赖它。

对比维度 自动埋点(Instrumentation Agent) 手动埋点(Manual Instrumentation)
实现方式 通过字节码增强、AOP 改写类库 开发者显式调用 API 创建 Span
覆盖范围 常见类库(Spring、gRPC、MySQL) 任意业务代码
维护成本 低,升级 SDK 即可 高,需要写业务语义
业务信息 只有 HTTP 方法、URL、状态码等通用信息 可以携带订单 ID、用户 ID 等关键字段

你看完这个表应该明白了。自动埋点帮你省了 80% 的功夫,但剩下那 20% 往往才是排查问题最需要的信息。比如订单超时了,你光知道请求去了 A 服务、B 服务,这不够。你还要知道是哪个订单、哪个用户——这些必须通过手动埋点加上Span Attributes。

⚠️ 注意: 不要把所有业务字段都塞进 Span。我曾经见过有人把整个请求体 JSON 写进 Attributes……那个 Span 大到可以直接当文档存储。建议只保留 业务 ID、错误码、关键耗时,控制在 5~10 个以内。

3.3 Exporter:数据怎么送出去?

SDK 采集完数据,总不能就地存着。Exporter 就是把数据发往 Collector 或者后端的通道。常见的导出方式有这么几种:

  1. OTLP Exporter——官方推荐的格式,支持 gRPC 和 HTTP。我基本只用它,因为它是全链路二进制协议,传输效率高。
  2. Jaeger Exporter——如果你后端是 Jaeger 的旧版本,可以直接用。但 OTel 官方也建议逐渐迁移到 OTLP。
  3. Logging Exporter——纯调试用的,把 Span 打印到控制台。初学的时候可以用它验证埋点有没有生效。

配置 Exporter 的时候,有一件事特别容易翻车——超时和重试。默认配置可能只有 5 秒超时,如果你后端偶尔波动一下,数据就丢了。我这里给一个生产常用的配置示例:

otlp:
  exporter:
    endpoint: "http://otel-collector:4317"
    tls:
      insecure: true
    timeout: 15s
    retry:
      initial_interval: 500ms
      max_interval: 30s
      max_elapsed_time: 120s
    compression: gzip

你看,我习惯把超时拉到 15 秒,加上指数退避重试。这样即使 Collector 短暂不可用,数据也不会一下子全丢了。

3.4 Collector:生产环境的标配

很多人不理解:SDK 直接发到后端不就行了,为什么中间非要加一层 Collector?其实,Collector 是全链路监控中最重要的"阀门"。

它主要由三部分组成:

  • Receivers: 接收数据。可以同时接收 OTLP、Jaeger、Prometheus 等多种格式的数据。
  • Processors: 处理数据。可以重采样、添加资源属性、过滤掉健康检查等无效 Span。我最常用的是 batch 和 attributes 两个 Processor。
  • Exporters: 导出数据到后端存储。可以同时写 Elasticsearch、ClickHouse、Prometheus 等多处。

核心价值: Collector 充当了 SDK 与存储之间的缓冲区。它帮你解耦了数据生产端和数据消费端。你换后端、加采样策略、做数据去重,都只改 Collector 配置就行,业务代码完全不需要动。

举个例子。我们团队曾经接入过监控系统,最开始用的自建 Jaeger,后来团队决定统一迁到 Grafana Tempo。如果没有 Collector,那每个服务都要改 Exporter 配置和重启。但因为我们用了 OTel Collector,把后端出口从 Jaeger 改到 Tempo,只改了一行 Collector 的 Exporter 配置,所有服务都不用动。 这就是 Collector 的价值所在。

3.5 生产落地的小结

我花了两个项目才把 OTel 这套整明白。给你几个可以直抄的结论:

  • SDK 选择:用官方 SDK,不要用厂商改版本。优先用自动埋点,业务关键路径用手动埋点补亚迪。
  • Exporter:首推 OTLP(gRPC)。配上重试、压缩、合理超时。
  • Collector:强烈建议部署,哪怕单实例也行。它是你未来的扩展点。
  • 采样策略:生产环境不要全量采样。要么固定采样率,要么基于错误率自适应。

好了,OpenTelemetry 的骨架先讲到这儿。你手头如果有正在跑的应用,可以先试着手动埋一个 Span 看看控制台有没有输出。这个手感很重要。