采样决策传播
介绍
在分布式系统中,追踪(Tracing)是理解请求在多个服务间流转的关键工具。Jaeger 作为流行的分布式追踪系统,通过采样决策传播(Sampling Decision Propagation)机制,确保一个请求的采样状态在其经过的所有服务中保持一致。本文将从基础概念讲起,逐步解析这一机制的原理和实现方式。
什么是采样决策传播?
采样决策传播是指:当一个请求进入系统时,第一个接触的服务(通常是入口服务,如 API 网关)会决定是否对该请求进行采样(即记录追踪数据),并将这一决策传递给后续的所有服务。这样,整个调用链要么全部被采样,要么全部不被采样,避免数据不完整。
为什么需要采样?
在高流量的生产环境中,记录所有请求的追踪数据会消耗大量资源。采样策略(如固定速率采样、动态采样)帮助我们在资源消耗和数据完整性之间取得平衡。
工作原理
1. 采样决策的生成
入口服务根据配置的采样策略(如 probabilistic
)决定是否采样。例如:
go
// 示例:Jaeger 客户端配置概率采样(50% 采样率)
sampler := jaeger.NewProbabilisticSampler(0.5)
tracer, _ := jaeger.NewTracer(
"my-service",
sampler,
jaeger.NewNullReporter(),
)
2. 决策的传递
决策通过 HTTP 头(如 uber-trace-id
)或 RPC 上下文传递给下游服务。例如:
uber-trace-id: 1a2b3c4d5e6f7g8h:1a2b3c4d5e6f7g8h:0:1
末尾的 1
表示“已采样”,0
表示“未采样”。
3. 下游服务的响应
下游服务解析头部并遵循相同的采样决策,不再重复决策。
实际案例
假设一个电商系统的请求流程:
- API 网关收到
/checkout
请求,决定采样(概率 50%)。 - 网关调用 订单服务,并在 HTTP 头中传递
uber-trace-id: ...:1
。 - 订单服务调用 库存服务,透传相同的头部。
- 所有相关 Span(网关、订单、库存)要么全部记录,要么全部忽略。
代码示例
以下是一个 Python 服务的示例,展示如何读取和传递采样决策:
python
from flask import Flask, request
import opentracing
app = Flask(__name__)
tracer = opentracing.global_tracer()
@app.route('/process', methods=['POST'])
def process():
# 从 HTTP 头中提取追踪上下文
span_ctx = tracer.extract(
format=opentracing.Format.HTTP_HEADERS,
carrier=request.headers
)
# 创建子 Span(继承父 Span 的采样决策)
with tracer.start_span('process-data', child_of=span_ctx) as span:
span.log_kv({'event': 'data_processed'})
return "Done"
常见问题
决策不一致的风险
如果某个服务错误地覆盖了采样头部,会导致调用链数据不完整。务必使用官方客户端库,避免手动修改头部。
总结
- 采样决策传播确保分布式调用链的追踪数据完整性。
- 决策由入口服务生成,通过上下文(如 HTTP 头)传递。
- 所有服务必须尊重并透传原始决策。
扩展练习
- 使用 Jaeger 官方示例(如 HotROD)观察采样决策的传递过程。
- 尝试在本地环境中配置一个概率采样率为 10% 的服务链,并验证采样结果。