纯技术

go-zero:链路追踪

横跨几十个分布式组件的慢请求要如何排查,我们可能会想到用request_id将多个服务器上的日志串起来,但仅仅依靠 requestId 很难表达清楚服务之间的调用关系,所以从日志中就无法了解服务之间是谁在调用谁 因此,我们采用 traceI…

4 分钟阅读
精选技术记录
持续迭代更新
Share

spanContext

横跨几十个分布式组件的慢请求要如何排查,我们可能会想到用request_id将多个服务器上的日志串起来,但仅仅依靠 requestId 很难表达清楚服务之间的调用关系,所以从日志中就无法了解服务之间是谁在调用谁

因此,我们采用 traceId + spanId 这两个数据维度来记录服务之间的调用关系(这里 traceId 就是 requestId),也就是使用 traceId 串起单次请求,用 spanId 记录每一次 RPC 调用

image.png

通过这种方式,我们可以在日志中清晰地看出服务的调用关系是如何的,方便在后续计算中调整日志顺序,打印出完整的调用链路

spanId 是何时生成的,如何传递的

A 服务在发起 RPC 请求服务 B 前,先从线程上下文中获取当前的 traceId 和 spanId,然后依据上面的逻辑生成本次 RPC 调用的 spanId,再将 spanId 和 traceId 序列化后装配到请求体中,发送给服务方 B

服务方 B 获取请求后,从请求体中反序列化出 spanId 和 traceId,同时设置到线程上下文中,以便给下次 RPC 调用使用。在服务 B 调用完成返回响应前,计算出服务 B 的执行时间发送给消息队列

Go-Zero中的使用

在/etc/{your-service}.yaml下添加

Telemetry:
  Name: your service name
  Endpoint: http://127.0.0.1:14268/api/traces
  Sampler: 1.0
  Batcher: jaeger

配置完成后,重启,go-zero 会按拦截器的形式往ctx中注入trace_id 和span_id

rpc

// UnaryTracingInterceptor returns a grpc.UnaryClientInterceptor for opentelemetry.
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
}

参考

github.com/zeromicro/g…


本文同步自掘金

如果发现内容有误或需要更新,请访问掘金原文进行查看。

Share

如果这类内容对你有帮助

这里放一个阿里云 AIGC 活动入口。只有在你本身就有相关需求时,再通过它了解即可;如果产生推广收益,我会优先用于支付服务器、域名和网站维护费用。

看看阿里云 AIGC 活动

相关文章

2025年8月21日

为什么有的人学了很多年的编程,还是只会【增删改查】?

为什么你写了多年代码,技术栈还停留在"增删改查"? 我们不妨从一个常见的开发者画像开始:工作数年,日常任务是理解需求、实现业务逻辑、提供数据接口。在熟悉的框架下,每天熟练地进行着数据库的增、删、改、查(CURD)操作,周而复始。 这套流程看…

2023年12月21日

Golang-常用限流算法实现

常用的限流算法有一下4中实现方式: 令牌桶 漏桶 计数器 滑动窗口 令牌桶以恒定的速度向桶里加入令牌,桶满了则不再加入令牌。当服务收到请求时尝试从桶中取出一个令牌,如果可以获取到令牌,则继续执行后续的业务,否则返回超限错误码或对应的错误页面…

2023年12月21日

go-zero 使用 Consul 作为注册中心

go-zero 默认使用etcd作为注册中心,如果我们的业务使用的consul, 可以使用zero-contrib中的consul包进行替代 文件位置:ect/**.yaml 增加consul配置 服务启动后即可在conusl/ui上查看到…