go-zero链路追踪

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

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

    代码结构

    • :保存链路的上下文信息「traceid,spanid,或者是其他想要传递的内容」
    • span :链路中的一个操作,存储时间和某些信息
    • trace 传播下游的操作「抽取,注入」
    • noop :实现了空的 tracer 实现

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

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

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

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

    实例应用

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

    1. 将 header -> carrier,获取 header 中的traceId等信息
    2. 从上述的 carrier「也就是header」获取traceId,spanId
      • 看header中是否设置
      • 如果没有设置,则随机生成返回
    3. request 中产生新的ctx,并将相应的信息封装在 ctx 中,返回
    4. 从上述的 context,拷贝一份到当前的 request

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

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

    1. 获取上游带下来的 span 上下文信息
    2. 从获取的 span 中创建新的 ctx,span「继承父span的traceId」

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

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

    参考