z.cn/wangzhan/weihu/"> 网站运营维护
  • 案例

  • 方案

  • 电商网站开发

  • 微信小程序

  • 我们

  • 联系

  • 资讯

    精准传达 • 有效沟通

    从品牌网站建设到网络营销策划,从策略到执行的一站式服务

    go-zero中怎么追踪请求链路的

    这篇文章给大家介绍go-zero中怎么追踪请求链路的,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

    专注于为中小企业提供网站设计制作、网站建设服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业内蒙古免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了上千多家企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。

    go-zero 是如何追踪你的请求链路

    微服务架构中,调用链可能很漫长,从 httprpc ,又从 rpchttp 。而开发者想了解每个环节的调用情况及性能,最佳方案就是 全链路跟踪

    追踪的方法就是在一个请求开始时生成一个自己的 spanID ,随着整个请求链路传下去。我们则通过这个 spanID 查看整个链路的情况和性能问题。

    下面来看看 go-zero 的链路实现。

    代码结构

    • spancontext:保存链路的上下文信息「traceid,spanid,或者是其他想要传递的内容」

    • span:链路中的一个操作,存储时间和某些信息

    • propagator: trace 传播下游的操作「抽取,注入」

    • noop:实现了空的 tracer 实现

    go-zero中怎么追踪请求链路的

    概念

    SpanContext

    在介绍 span 之前,先引入 context 。SpanContext 保存了分布式追踪的上下文信息,包括 Trace id,Span id 以及其它需要传递到下游的内容。OpenTracing 的实现需要将 SpanContext 通过某种协议 进行传递,以将不同进程中的 Span 关联到同一个 Trace 上。对于 HTTP 请求来说,SpanContext 一般是采用 HTTP header 进行传递的。

    下面是 go-zero 默认实现的 spanContext

    type spanContext struct {
        traceId string      // TraceID 表示tracer的全局唯一ID
        spanId  string      // SpanId 标示单个trace中某一个span的唯一ID,在trace中唯一
    }

    同时开发者也可以实现 SpanContext 提供的接口方法,实现自己的上下文信息传递:

    type SpanContext interface {
        TraceId() string                        // get TraceId
        SpanId() string                         // get SpanId
        Visit(fn func(key, val string) bool)    // 自定义操作TraceId,SpanId
    }

    Span

    一个 REST 调用或者数据库操作等,都可以作为一个 spanspan 是分布式追踪的最小跟踪单位,一个 Trace 由多段 Span 组成。追踪信息包含如下信息:

    type Span struct {
        ctx           spanContext       // 传递的上下文
        serviceName   string            // 服务名 
        operationName string            // 操作
        startTime     time.Time         // 开始时间戳
        flag          string            // 标记开启trace是 server 还是 client
        children      int               // 本 span fork出来的 childsnums
    }

    span 的定义结构来看:在微服务中, 这就是一个完整的子调用过程,有调用开始 startTime ,有标记自己唯一属性的上下文结构 spanContext 以及 fork 的子节点数。

    实例应用

    go-zero 中http,rpc中已经作为内置中间件集成。我们以 http,rpc 中,看看 tracing 是怎么使用的:

    HTTP

    func TracingHandler(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            // **1**
            carrier, err := trace.Extract(trace.HttpFormat, r.Header)
            // ErrInvalidCarrier means no trace id was set in http header
            if err != nil && err != trace.ErrInvalidCarrier {
                logx.Error(err)
            }
    
            // **2**
            ctx, span := trace.StartServerSpan(r.Context(), carrier, sysx.Hostname(), r.RequestURI)
            defer span.Finish()
            // **5**
            r = r.WithContext(ctx)
    
            next.ServeHTTP(w, r)
        })
    }
    
    func StartServerSpan(ctx context.Context, carrier Carrier, serviceName, operationName string) (
        context.Context, tracespec.Trace) {
        span := newServerSpan(carrier, serviceName, operationName)
        // **4**
        return context.WithValue(ctx, tracespec.TracingKey, span), span
    }
    
    func newServerSpan(carrier Carrier, serviceName, operationName string) tracespec.Trace {
        // **3**
        traceId := stringx.TakeWithPriority(func() string {
            if carrier != nil {
                return carrier.Get(traceIdKey)
            }
            return ""
        }, func() string {
            return stringx.RandId()
        })
        spanId := stringx.TakeWithPriority(func() string {
            if carrier != nil {
                return carrier.Get(spanIdKey)
            }
            return ""
        }, func() string {
            return initSpanId
        })
    
        return &Span{
            ctx: spanContext{
                traceId: traceId,
                spanId:  spanId,
            },
            serviceName:   serviceName,
            operationName: operationName,
            startTime:     timex.Time(),
            // 标记为server
            flag:          serverFlag,
        }
    }
    1. 将 header -> carrier,获取 header 中的traceId等信息

    2. 开启一个新的 span,并把**「traceId,spanId」**封装在context中

    3. 从上述的 carrier「也就是header」获取traceId,spanId。

      • 看header中是否设置

      • 如果没有设置,则随机生成返回

    4. request 中产生新的ctx,并将相应的信息封装在 ctx 中,返回

    5. 从上述的 context,拷贝一份到当前的 request

    go-zero中怎么追踪请求链路的

    这样就实现了 span 的信息随着 request 传递到下游服务。

    RPC

    在 rpc 中存在 client, server ,所以从 tracing 上也有 clientTracing, serverTracingserveTracing 的逻辑基本与 http 的一致,来看看 clientTracing 是怎么使用的?

    func TracingInterceptor(ctx context.Context, method string, req, reply interface{},
        cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
        // open clientSpan
        ctx, span := trace.StartClientSpan(ctx, cc.Target(), method)
        defer span.Finish()
    
        var pairs []string
        span.Visit(func(key, val string) bool {
            pairs = append(pairs, key, val)
            return true
        })
        // **3** 将 pair 中的data以map的形式加入 ctx
        ctx = metadata.AppendToOutgoingContext(ctx, pairs...)
    
        return invoker(ctx, method, req, reply, cc, opts...)
    }
    
    func StartClientSpan(ctx context.Context, serviceName, operationName string) (context.Context, tracespec.Trace) {
        // **1**
        if span, ok := ctx.Value(tracespec.TracingKey).(*Span); ok {
            // **2**
            return span.Fork(ctx, serviceName, operationName)
        }
    
        return ctx, emptyNoopSpan
    }
    1. 获取上游带下来的 span 上下文信息

    2. 从获取的 span 中创建新的 ctx,span「继承父span的traceId」

    3. 将生成 span 的data加入ctx,传递到下一个中间件,流至下游

    总结

    go-zero 通过拦截请求获取链路traceID,然后在中间件函数入口会分配一个根Span,然后在后续操作中会分裂出子Span,每个span都有自己的具体的标识,Finsh之后就会汇集在链路追踪系统中。

    开发者可以通过 ELK 工具追踪 traceID ,看到整个调用链。同时 go-zero 并没有提供整套 trace 链路方案,开发者可以封装 go-zero 已有的 span 结构,做自己的上报系统,接入 jaeger, zipkin 等链路追踪工具。

    关于go-zero中怎么追踪请求链路的就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。


    本文题目:go-zero中怎么追踪请求链路的
    网页网址:http://cdkjz.cn/article/jihghs.html
    多年建站经验

    多一份参考,总有益处

    联系快上网,免费获得专属《策划方案》及报价

    咨询相关问题或预约面谈,可以通过以下方式与我们联系

    大客户专线   成都:13518219792   座机:028-86922220