深色模式
分布式追踪 Tracing
摘要:分布式追踪把一次用户请求在所有服务里的执行过程串成一棵树。本文讲清 Trace/Span/Context 传播三个核心概念,给出 W3C traceparent 头的手动验证方法、采样策略配置,以及让链路真正有用的属性规范。
适用环境
bash
# 确认已有后端接收链路(Tempo/Jaeger 任选)
curl -fsS http://127.0.0.1:3200/ready # Tempo
curl -fsS http://127.0.0.1:16686/ # Jaeger UI
# 准备两个互调的服务用于验证传播
kubectl get svc 2>/dev/null | head
curl -s http://127.0.0.1:8080/api/order -o /dev/null -w '%{http_code}\n'1
2
3
4
5
6
7
2
3
4
5
6
7
操作步骤
1. 理解核心概念
- Trace:一次请求的全链路,用全局唯一
trace_id(32 位十六进制)标识。 - Span:链路中的一段工作单元,有
span_id、父span_id、开始时间、耗时、属性、事件与状态。 - Context 传播:把
trace_id/span_id通过请求头传给下游,是跨服务串联的关键。
2. 认识 W3C TraceContext 头
bash
# traceparent 格式:00-<32位trace_id>-<16位span_id>-<flags>
# 示例
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
# flags 末位 01 表示 sampled(被采样)1
2
3
4
2
3
4
3. 手动验证传播是否生效
bash
# 自己构造一个 traceparent 打进去,看下游是否沿用同一个 trace_id
TRACE_ID=4bf92f3577b34da6a3ce929d0e0e4736
curl -s -H "traceparent: 00-${TRACE_ID}-00f067aa0ba902b7-01" \
http://127.0.0.1:8080/api/order -o /dev/null
# 在后端按该 trace_id 查询,应能看到完整调用树
curl -s "http://127.0.0.1:3200/api/traces/${TRACE_ID}" | head -c 3001
2
3
4
5
6
7
2
3
4
5
6
7
4. 配置采样策略(不采样就没有链路,全采样则成本过高)
yaml
# Collector 的尾部采样:保留错误链路和慢链路,丢弃普通成功链路
processors:
tail_sampling:
decision_wait: 10s
policies:
- name: keep-errors
type: status_code
status_code: {status_codes: [ERROR]}
- name: keep-slow
type: latency
latency: {threshold_ms: 1000}
- name: keep-sample
type: probabilistic
probabilistic: {sampling_percentage: 5}1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
bash
# SDK 侧头部采样(更省资源,但无法"事后补救")
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.051
2
3
2
3
5. 规范 span 属性(决定链路能不能被检索和分析)
python
span.set_attribute("http.method", "POST")
span.set_attribute("http.route", "/api/order")
span.set_attribute("db.system", "mysql")
span.set_attribute("order.amount", 199.0)
span.set_attribute("user.tier", "vip")
# 错误必须记录
span.record_exception(exc)
span.set_status(Status(StatusCode.ERROR, str(exc)))1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
6. 用 span 事件标记关键步骤
python
span.add_event("payment.called", {"gateway": "alipay"})
span.add_event("payment.callback_received")1
2
2
7. 接入 Baggage 传递业务上下文
bash
# baggage 会沿调用链自动透传,适合传递租户、灰度标识等
curl -s -H "baggage: tenant=acme,canary=true" http://127.0.0.1:8080/api/order -o /dev/null1
2
2
验证
bash
# 1) 后端能查到 trace
curl -s 'http://127.0.0.1:3200/api/search?limit=5' | head -c 200
# 2) 一条 trace 包含多个服务的 span(说明传播成功)
curl -s "http://127.0.0.1:3200/api/traces/${TRACE_ID}" \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d['batches']))"
# 3) 响应头是否回传了 trace 信息(便于前端串联)
curl -sI http://127.0.0.1:8080/api/order | grep -i 'traceparent'
# 4) 错误链路是否被采样保留(触发一个 500 再查)
curl -s http://127.0.0.1:8080/api/order?fail=1 -o /dev/null1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
常见坑
WARNING
链路断成两截,绝大多数是因为中间某环没有传递 context:异步线程、线程池、消息队列、定时任务都会丢失上下文。使用 OTel 提供的 context 传递工具显式搬运。
WARNING
只做了头部采样(如 1%)时,错误请求很可能恰好没被采样,导致"用户报错但查不到链路"。建议配合尾部采样保留错误与慢请求。
DANGER
把用户 ID、手机号、完整 SQL 参数写进 span 属性,这些数据会随链路长期存储并可能被多人查看。属性中只允许出现脱敏后的业务标识。