当微服务拆分到几十个、调用链横跨十几个节点时,一次慢请求到底卡在哪里?OpenTelemetry 链路追踪给出的答案,是用一套厂商无关的标准,把一次请求在分布式系统中的完整路径——从网关、订单服务到数据库——串成一条可视化的调用链(Trace)。它不绑定任何后端,既能对接 Jaeger,也能把数据送进你已有的 Prometheus 与 Loki 体系,是补齐可观测性三支柱的关键一环。
一、为什么需要链路追踪
指标(Metrics)告诉你“系统变慢了”,日志(Logs)告诉你“某行报错了”,但只有链路追踪能回答“这一次请求经过了哪些服务、各自耗时多少”。在单体应用里靠日志 grep 还能勉强排查;在微服务架构下,没有 TraceID 贯穿上下文,跨服务排障基本靠猜。链路追踪的核心概念只有三个:Trace(一次完整请求)、Span(其中的一个处理单元)、Context Propagation(跨进程传递 TraceID)。
举个真实场景:用户反馈“下单偶尔要 8 秒”。你查 Prometheus 发现订单服务 P99 只有 800ms,数据库也很平稳——但用户端就是慢。最后定位到,订单服务在调用“库存”和“优惠券”两个下游时,其中一个依赖了早已下线的老服务,串行调用又没设超时,把整条链路拖死。没有 Trace,这种“单点慢、整体崩”的问题几乎无法定位;有了 Trace,你一眼就能看到是哪个 span 耗掉了 7 秒。
二、OpenTelemetry 是什么:三支柱的统一标准
OpenTelemetry(简称 OTel)是 CNCF 的毕业级项目,目标是用一套 API、SDK 和采集协议,统一 Metrics、Logs、Traces 的采集方式。你不再需要为每种后端写不同的埋点代码——应用只管产出规范的遥测数据,由 OTel Collector 决定转发到哪里。这种“采集与后端解耦”的设计,正是它击败旧方案(如各自为政的 Zipkin、SkyWalking 私有协议)的原因。
更关键的是它的线协议 OTLP(OpenTelemetry Protocol)。所有语言 SDK 产出的数据都序列化成同一种 OTLP 格式,经 gRPC(4317)或 HTTP(4318)发给 Collector。这意味着你今天把后端从 Jaeger 换成 TempO 或商业 APM,应用侧一行代码都不用改——换的只是 Collector 的 exporter 配置,这正是“可观测性不被厂商锁定”的底层保障。
三、快速搭建:用 Collector 串起采集管道
最轻量的落地方式是用 Docker Compose 拉起一个 OTel Collector,它负责接收应用上报的 trace,再转发到后端(这里以 Jaeger 做存储与查询界面)。
# docker-compose.yml
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
command: ["--config=/etc/otel/config.yaml"]
volumes:
- ./otel-config.yaml:/etc/otel/config.yaml
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
jaeger:
image: jaegertracing/all-in-one:latest
ports:
- "16686:16686" # 查询界面
Collector 的配置把“接收(receivers)—处理(processors)—导出(exporters)”三段式管道写清楚:
# otel-config.yaml
receivers:
otlp:
protocols:
grpc:
http:
processors:
batch:
memory_limiter:
check_interval: 1s
limit_mib: 512
exporters:
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp/jaeger]
四、应用埋点:自动插桩省去手写
OTel 提供“自动插桩”,无需改动业务代码即可采集 Web 框架、数据库、HTTP 客户端的 span。以 Python(FastAPI)为例,安装对应 instrumentor 后在入口启用即可:
# instrumentation.py
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from fastapi import FastAPI
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter(endpoint="http://otel-collector:4317")))
trace.set_tracer_provider(provider)
app = FastAPI()
FastAPIInstrumentor.instrument_app(app) # 自动为路由生成 span
RequestsInstrumentor().instrument() # 自动追踪下游 HTTP 调用,保持 TraceID 传播
Java 侧更简单,用官方 Java Agent 在启动参数挂上即可,连代码都不用改:-javaagent:opentelemetry-javaagent.jar -Dotel.exporter.otlp.endpoint=http://otel-collector:4317。Agent 会自动为 Spring、JDBC、Kafka 等常见组件生成 span,并把 TraceID 注入到跨服务调用的 HTTP header 中。
如果自动插桩覆盖不到你的自定义逻辑(比如一段耗时计算或外部 RPC),可以手动创建一个 span 把它单独框出来,方便定位热点:
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("heavy_computation") as span:
span.set_attribute("input.size", len(payload))
span.set_attribute("user.tier", "vip")
result = do_expensive_work(payload) # 这段耗时会被单独记录为一个 span
span.set_attribute("result.count", len(result))
五、与 Prometheus、Loki 组成可观测性三支柱
链路追踪不是替代指标和日志,而是与它们相互印证。一个典型的排障链路是:Prometheus 告警“订单接口 P99 升高” → 在 OTel 里按服务名检索慢 Trace,定位到具体慢 span → 用该 span 的 TraceID 去 Loki 检索关联日志。三者通过统一标签(如 service.name、trace_id)关联,才能形成闭环。在已用 Kubernetes 编排服务、用 GitHub Actions 跑 CI/CD、并以 Terraform 管理基础设施的体系里,OpenTelemetry 正好补齐最后一块观测拼图。
| 支柱 | 回答的问题 | 本站已有实践 |
|---|---|---|
| Metrics(指标) | 系统整体是否健康、有无异常波动 | Prometheus + Grafana 监控面板 |
| Logs(日志) | 某条记录里到底发生了什么 | Loki / ELK 轻量级日志收集 |
| Traces(链路) | 这一次请求卡在哪个服务、耗时多少 | 本文 OpenTelemetry 链路追踪 |
六、生产落地检查清单
上线前务必核对以下要点,否则链路追踪很容易变成“能看但不能用”的花架子。
| 检查项 | 说明 | 常见坑 |
|---|---|---|
| 采样策略 | 高流量服务务必开启头采样/尾部采样,避免 trace 数据爆炸 | 全量上报把 Collector 和存储打满 |
| 上下文传播 | 跨进程(HTTP/gRPC/消息队列)必须携带 TraceID | MQ 消费侧丢失 TraceID,链路断裂 |
| 统一资源属性 | 所有服务上报一致的 service.name、environment 标签 | 标签不统一,聚合查询失效 |
| 与告警联动 | 慢 Trace 触发告警,而非只看面板 | 只有可视化、没有闭环告警 |
七、上下文传播:W3C Trace Context 标准
链路能跨进程连起来的关键,是 OTel 默认遵循 W3C Trace Context 标准:它在每个 HTTP 请求头里注入一个名为 traceparent 的字段,把 TraceID 和上游 SpanID 带下去。下游服务读取这个头,就能把自己的 span 挂到同一条 Trace 上。只要所有服务都用 OTel(或兼容该标准),哪怕技术栈混合——Java 调 Python 再调 Go——链路也不会断。这也是为什么前文强调“上下文传播”必须打通:一旦 MQ 消费侧没把 TraceID 透传,链路就会在这里断成两截。
# 网关发出的请求头示例
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
# 含义:版本 - TraceID - 父SpanID - 采样标志(01=被采样)
八、小结
OpenTelemetry 把链路追踪从“私有协议孤岛”拉进了统一标准时代。你只需在应用层接入一次,后续换后端、接告警、关联日志都不再受绑定。配合已有的 Prometheus 指标与 Loki 日志,可观测性三支柱就此闭环——下一次线上变慢,你不再靠猜,而是顺着一条 Trace 直达根因。
对于刚起步的团队,建议先从一个核心服务接入 OTel,跑通“采集—查看—关联日志”的最小闭环,再逐步铺开,避免一开始就全网埋点带来的存储与认知负担。




