追踪上下文传递
介绍
在分布式系统中,一个请求可能会跨越多个服务。为了完整追踪整个请求链路,我们需要在服务间传递追踪上下文(Trace Context)。Jaeger通过特定的HTTP头或RPC上下文来传递这些信息,确保所有相关操作属于同一个逻辑事务。
关键概念
- Trace ID:唯一标识一个分布式追踪链。
- Span ID:标识当前操作的片段。
- Parent Span ID:指向父级操作(用于构建层级关系)。
上下文传递机制
1. HTTP头传递
当服务通过HTTP通信时,Jaeger会将上下文注入以下标准头中:
uber-trace-id
:包含Trace ID、Span ID、Parent Span ID和采样标志uberctx-{key}
:用于传递自定义Baggage(跨服务键值数据)
示例请求头:
http
GET /api/data HTTP/1.1
Host: example.com
uber-trace-id: 3a3c43f33a3c43f3:1e3443a4:0:1
uberctx-user-id: 12345
2. 代码示例(Node.js)
发送请求时携带上下文
javascript
const { initTracer } = require('jaeger-client');
const axios = require('axios');
// 初始化追踪器
const tracer = initTracer(config);
async function fetchData() {
const span = tracer.startSpan('call_external_api');
// 将当前span上下文注入headers
const headers = {};
tracer.inject(span.context(), FORMAT_HTTP_HEADERS, headers);
try {
const response = await axios.get('http://service-b/api', { headers });
span.finish();
return response.data;
} catch (err) {
span.setTag('error', true);
span.log({ 'error.message': err.message });
span.finish();
throw err;
}
}
接收请求时提取上下文
javascript
app.get('/api', (req, res) => {
// 从HTTP头提取上下文
const parentSpanContext = tracer.extract(FORMAT_HTTP_HEADERS, req.headers);
// 创建子span
const span = tracer.startSpan('process_request', {
childOf: parentSpanContext
});
// 业务逻辑...
span.finish();
res.send('Data processed');
});
3. 上下文传递流程
实际应用场景
电商订单处理流程:
- 用户下单(前端服务)
- 扣减库存(库存服务)
- 创建订单(订单服务)
- 支付处理(支付服务)
通过上下文传递,我们可以:
- 追踪整个订单生命周期的性能
- 定位具体失败的服务
- 分析跨服务调用的延迟
调试技巧
在开发环境中,可以通过Jaeger UI查看完整的调用链,特别关注:
- 各Span的时间消耗
- 错误标记(红色标记)
- Baggage中传递的业务数据
总结
追踪上下文传递是分布式追踪的核心机制,它使得:
- 跨服务调用能够关联到同一个追踪链
- 系统行为可视化成为可能
- 性能分析和故障排查更加高效
扩展练习
- 尝试在两个微服务间手动传递追踪上下文(不使用SDK自动注入)
- 在Jaeger UI中观察Baggage的传递效果
- 模拟一个调用失败场景,观察错误如何在整个调用链中传播