本页目录
分布式追踪
一次请求跨越十几个微服务后,怎么知道时间花在了哪?分布式追踪给每个请求分配一个 trace ID,跨服务的调用关系通过 context propagation 传递 span context(trace ID + parent span ID + 采样标志),把散落在各节点的片段拼成完整调用链。采样策略是成本控制的关键:全量采太贵,固定比例采会漏掉长尾错误,tail-based 采样在完成后再决定保留哪些 trace。
Dapper (Google 2010) → OpenTelemetry
Google 的 Dapper 论文启发了开源生态:Zipkin (Twitter) → Jaeger (Uber) → OpenTracing + OpenCensus → OpenTelemetry (CNCF, 当前标准)。
Trace 与 Span
Context Propagation
跨服务的 trace 传递是关键——每个服务必须知道"我是哪个 trace 的哪个 span":
HTTP: traceparent: 00-<trace_id>-<span_id>-<trace_flags>
W3C Trace Context (标准): traceparent header
gRPC: metadata (grpc-trace-bin)
Kafka: message headers
trace_flags: bit 0 = SAMPLED (是否被采样), 其他 bits reserved。
内部实现:tracer 在调用下游服务时,创建新的 span (作为 caller span 的 child),将 trace_id + new_span_id 放入 outbound headers。下游的中间件/库(HTTP client, gRPC stub, DB driver)在收到 response 时提取 trace context,自动创建 child span。
Sampling
全量采集 → 开销不可接受(每条 span 需要 ~1μs overhead + 网络/存储成本)。需要采样策略:
- Head-based (概率): 在 trace 开始时决定是否采样(随机 1/100 或 1/1000)。简单,但可能漏掉包含错误的 trace。
- Tail-based (智能): 缓存所有 spans(本地 buffer),trace 完成后根据 result (ERROR? latency > SLO?) 决定是否保留。能保 error 不丢,但内存开销大。OpenTelemetry 的
tailsamplingprocessor 实现此策略。
Jaeger 部署 (参考)
# Agent (每 host 一个, 接收 app 通过 UDP 发来的 spans)
# Collector (中心, 从 agents 收 → 写入存储)
# Storage (Cassandra/Elasticsearch)
# Query (UI + API)
参考
- 论文: "Dapper, a Large-Scale Distributed Systems Tracing Infrastructure" (Google, 2010)
- OpenTelemetry: opentelemetry.io/docs/specs/otel/trace
- Jaeger: jaegertracing.io
Keywords: distributed tracing, Dapper, OpenTelemetry, Span, trace context propagation, W3C Trace Context, sampling