在单体应用时代,排查性能问题只需查看一台服务器的日志。但在微服务架构中,一个用户请求可能流经十几个甚至几十个服务节点,如何追踪请求的完整路径、定位性能瓶颈?在十三Tech 的分布式系统中,链路追踪是我们排查跨服务慢请求的标配工具。本文将从 traceId 和 spanId 的设计原理讲起,带你掌握 go-zero 中分布式链路追踪的实战配置。
为什么需要链路追踪
仅靠 request_id 可以将多个服务器上的日志串联起来,但 request_id 只能标识"这是一次请求",却无法表达服务之间的调用关系。从日志中你无法直观地看出:A 服务调用了 B 服务,B 服务又调用了 C 服务,而 C 服务的响应慢导致了整体延迟。
因此,业界普遍采用 traceId + spanId 两个维度来记录调用关系:
- traceId:全局唯一标识,串联单次请求的完整链路(等价于 requestId)
- spanId:标识每一次 RPC 调用,记录调用方的开始时间、耗时、服务名等元信息
通过这种方式,我们可以在链路追踪系统中清晰地看到请求的服务拓扑、各节点的耗时分布,从而精准定位性能瓶颈。
spanId 的生成与传递机制
生成时机
当服务 A 向服务 B 发起 RPC 调用前,A 会从当前上下文中获取 traceId 和当前的 spanId,然后依据规则生成一个新的 spanId(代表本次 RPC 调用),将 traceId 和新 spanId 序列化后装配到请求体(通常是 HTTP Header 或 gRPC Metadata)中,发送给服务 B。
传递与恢复
服务 B 收到请求后,从请求体中反序列化出 traceId 和 spanId,并设置到本地线程上下文中,以便 B 在向下游发起调用时继续使用。B 处理完请求返回响应前,会计算本次调用的执行时间,将 span 信息上报到追踪系统(如 Jaeger、Zipkin)。
go-zero 中的链路追踪配置
go-zero 从 v1.4 版本开始内置了基于 OpenTelemetry 的链路追踪支持,配置非常简单。
配置文件
在服务的 etc/{your-service}.yaml 中添加 Telemetry 配置:
Telemetry:
Name: your-service-name
Endpoint: http://127.0.0.1:14268/api/traces
Sampler: 1.0
Batcher: jaeger
配置项说明:
Name:当前服务在链路追踪系统中的标识名Endpoint:追踪系统的接收端点,Jaeger 默认端口为 14268Sampler:采样率,1.0表示全量采样,0.1表示采样 10% 的请求Batcher:追踪后端类型,支持jaeger、zipkin等
配置完成后重启服务,go-zero 会以拦截器的形式自动往 ctx 中注入 trace_id 和 span_id。
RPC 拦截器实现
go-zero 在 RPC 客户端拦截器中完成了 trace 信息的自动传递:
// UnaryTracingInterceptor 返回一个基于 OpenTelemetry 的 gRPC 客户端拦截器
func UnaryTracingInterceptor(ctx context.Context, method string, req, reply any,
cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
ctx, span := startSpan(ctx, method, cc.Target())
defer span.End()
ztrace.MessageSent.Event(ctx, 1, req)
err := invoker(ctx, method, req, reply, cc, opts...)
ztrace.MessageReceived.Event(ctx, 1, reply)
if err != nil {
s, ok := status.FromError(err)
if ok {
span.SetStatus(codes.Error, s.Message())
span.SetAttributes(ztrace.StatusCodeAttr(s.Code()))
} else {
span.SetStatus(codes.Error, err.Error())
}
return err
}
span.SetAttributes(ztrace.StatusCodeAttr(gcodes.OK))
return nil
}
核心流程:
startSpan从上下文中提取父 span 信息,创建新的 span- 在请求发送前记录
MessageSent事件 - 执行实际的 RPC 调用
- 在响应接收后记录
MessageReceived事件 - 如果调用出错,将错误信息记录到 span 中
defer span.End()确保 span 信息被正确上报
采样策略的选择
在生产环境中,全量采样(Sampler: 1.0)会对系统性能和存储造成较大压力。建议根据场景选择合适的采样策略:
- 开发/测试环境:全量采样,便于调试
- 生产环境低峰期:全量或高比例采样
- 生产环境高峰期:低比例采样(如 1% ~ 10%),或结合基于延迟的自适应采样
- 关键路径:对核心交易链路单独配置高采样率
总结
分布式链路追踪是微服务架构可观测性的三大支柱之一(Metrics、Logging、Tracing)。go-zero 通过内置的 OpenTelemetry 支持,让开发者只需几行 YAML 配置即可获得企业级的链路追踪能力。在十三Tech 的生产实践中,链路追踪不仅帮助我们快速定位了多次跨服务性能问题,也为服务依赖分析、容量规划提供了宝贵的数据支撑。
如果你正在构建微服务系统却还没有引入链路追踪,现在就是最好的时机。