微服务之间如何传递 trace
526 字
3 分钟
微服务之间如何传递 trace
跨微服务时,靠的是“上下文传播”,不是接口 body 显式保留 trace id。
先区分 3 个东西:
- tracer:代码里
otel.Tracer(...)的对象,表示哪段代码在打点 - trace id:一整条链路的 ID
- span id:链路里某个节点的 ID
微服务之间传递的是 trace id/span 上下文,不是 tracer。
HTTP 场景
标准做法是放在请求头里,通常是 W3C Trace Context:
traceparenttracestate- 可选
baggage
流程是:
- 服务 A 收到请求,创建或继续一个 trace
- 服务 A 调服务 B 前,把当前 trace context 注入到 HTTP header
- 服务 B 收到请求后,从 header 提取 trace context
- 服务 B 基于这个 context 创建自己的 span
这样 A 和 B 的 span 就会落到同一个 trace 里。
所以一般不需要你在接口 JSON 里专门加:
{ "traceId": "..."}除非你有业务上的特殊需求。对 tracing 本身来说,不该这么做,原因是:
- 污染业务接口契约
- 容易和真实 OTel 上下文不一致
- 标准库/中间件已经能自动处理 header
HTTP 里通常像这样:
- 服务端:Extract(从入站 header 恢复上下文)
- 客户端:Inject(把上下文写入出站 header)
Go + OpenTelemetry 的服务端提取示例:
// 服务端:从入站请求 header 中提取上下文,再基于它创建 spanctx := otel.GetTextMapPropagator().Extract( r.Context(), propagation.HeaderCarrier(r.Header),)ctx, span := tracer.Start(ctx, "handle-request")defer span.End()客户端注入示例:
// 客户端:把当前上下文注入到出站请求 headerreq := httptest.NewRequest("GET", "http://service-b/api", nil)otel.GetTextMapPropagator().Inject( ctx, propagation.HeaderCarrier(req.Header),)gRPC 与消息队列
gRPC 也是一样,只是不是 HTTP header,而是 gRPC metadata。原理完全相同:
- client interceptor 注入
- server interceptor 提取
消息队列也类似,通常放在消息 header / metadata 里,不放消息 body。
实务建议
- HTTP:用
traceparent这些标准 header - gRPC:用 metadata
- MQ/Kafka:用 message headers
- 不要把 trace id 做成业务接口字段,除非只是为了调试展示
- 可以额外返回一个
X-Trace-ID给前端或调用方排查问题,但这只是便于观察,不是主链路传播的标准方式
相关概念见 OpenTelemetry 与链路追踪笔记。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
微服务之间如何传递 trace
https://blog.81vm3.xyz/posts/trace-propagation/相关文章智能推荐
1
微服务鉴权与网关职责
后端开发Gateway 负责认证、Service 负责授权:身份上下文透传、反例风险与文件服务落地场景。
2
一致性哈希:consistent 与 rendezvous 的对比
后端开发两种分布式哈希的均匀度与耗时对比结论:节点少选 rendezvous,节点多且时间敏感选 consistent。
3
Kafka 的分区与副本机制
后端开发Kafka Broker/Topic/分区的关系,以及 leader-follower 副本的读写与冗余备份机制。
4
本地负缓存
后端开发把不存在/失败的查询结果缓存在本地内存中,短时间内直接返回,避免重复打到下游系统。
5
MySQL 索引与慢查询优化
后端开发慢查询 200ms 阈值、JOIN 对数据量的影响、IN 列表过大丢索引与分批查询。
随机文章随机推荐











