go-zero通过TracingHandler中间件在HTTP入口提取traceID/spanID并注入Request.Context,RPC调用时自动注入至下游header/metadata,仅透传traceid、spanid、parentspanid三字段,需自定义Propagator扩展透传能力,严禁struct缓存ctx以防泄漏。

Go-Zero框架中,请求链路的上下文传递必须确保traceID、spanID等追踪信息跨HTTP和RPC边界不丢失,否则全链路跟踪会断裂。
HTTP入口处提取并初始化SpanContext
在HTTP handler最外层,go-zero自动调用trace.Extract从请求头中解析上游传递的trace信息。这一步由TracingHandler中间件完成,无需手动编写。
提取失败时(如header中无trace-id),会生成全新traceID和spanID;成功则复用上游值,保证链路连续。
提取后的carrier被传入trace.StartServerSpan,该函数创建新的server span,并将spanContext注入到http.Request.Context()中。
【r = r.WithContext(ctx) 这行不可省略,否则后续handler拿不到带trace信息的新ctx】
RPC调用时向下游注入SpanContext
当HTTP handler内部调用RPC服务(如userRpc.GetUser)时,go-zero的rpc client会自动执行注入逻辑。
方法一:使用自带client(推荐)
直接传入原始req.Context(),client内部通过trace.Inject把当前spanContext序列化为HTTP header或gRPC metadata,随请求发出。
方法二:手动注入(仅调试用)
调用trace.Inject(span.Context(), trace.HttpFormat, carrier),再把carrier内容写入metadata或header字段。
注意:若RPC服务未启用tracing中间件,下游将无法还原spanContext,链路在此中断。
跨服务时Context值的透传限制
go-zero默认只透传traceid、spanid、parentspanid三个关键字段,其他context.WithValue写入的数据不会自动跨进程传递。
第一步:确认业务需要透传的键名是否在trace.Propagator白名单中
第二步:若不在,需自定义Propagator实现,重写Inject和Extract方法
第三步:在服务启动时替换全局trace.SetPropagator
强行在HTTP header里塞入自定义key-value会导致下游服务解析失败或拒绝请求,尤其在多语言混合架构中风险极高。
避免Context泄漏的关键操作
绝不在struct字段中缓存context.Context,例如:type UserService struct { ctx context.Context }——这会导致goroutine长期持有已过期ctx,引发内存泄漏和超时失效。
所有异步任务(如日志上报、消息队列投递)必须显式传入派生ctx,且建议用context.WithTimeout(ctx, 3*time.Second)约束执行窗口。
HTTP handler返回前,确保所有子goroutine已收到ctx.Done()信号并退出,否则可能残留运行中goroutine绑定旧ctx。











