## 最近在`go-zero`中使用了一些三方包集成内部的链路追踪, 部分代码如下

```go
package main

import (
        "fmt"
        "github.com/zeromicro/go-zero/zrpc"
)

func main() {
       
        // 1. 获取服务依赖的配置
        svcCtx := svc.NewServiceContext()
       
        // 2. 实例化服务但配置, 实际上这里的代码会调用 otel.SetTracerProvider()
        // serfer.NewSerfer() =>
        // service.SetUp =>
        // trace.StartAgent =>
        // trace.startAgent() =>
        // otel.SetTracerProvider()
        s := zrpc.MustNewSerfer()
        defer s.Stop()


        fmt.Printf("Starting rpc serfer at %s...\n", svcCtx.Config.ListenOn)
        s.Start()
}

package svc

import (
    "time"

    "github.com/redis/go-redis/extra/redisotel/v9"
    "github.com/redis/go-redis/v9"
    "gorm.io/drifer/mysql"
    "gorm.io/gorm"
    "gorm.io/plugin/opentelemetry/tracing"
    "github.com/zeromicro/go-zero/zrpc"
)

func NewServiceContext() {

        conn, err := gorm.Open()
        redisClient := redis.NewClient()
       
        // 1.1 增加 链路追踪
        // 这以下两个方法都会调用 otel.GetTracerProvider()
        redisotel.InstrumentTracing(redisClient)
        conn.Use(tracing.NewPlugin(tracing.WithoutMetrics()))
       
        // return xxx
}
```

* [https://github.com/go-gorm/opentelemetry/blob/v0.1.6/tracing/tracing.go#L46]( https://github.com/go-gorm/opentelemetry/blob/v0.1.6/tracing/tracing.go#L46)
* [https://github.com/redis/go-redis/blob/v9.6.1/extra/redisotel/config.go#L57]( https://github.com/redis/go-redis/blob/v9.6.1/extra/redisotel/config.go#L57)

* 按理说肯定要先`Set`才能`Get`,而实际上`otel`采用了委托的方式让我们可以先`get`然后再`set`

## 代码追踪

* **otel**包的代码也很简单, 就是为了包装一层标准, 实际上是调用了**global**包

```go
package otel // import "go.opentelemetry.io/otel"

import (
        "go.opentelemetry.io/otel/internal/global"
        "go.opentelemetry.io/otel/trace"
)


func Tracer(name string, opts ...trace.TracerOption) trace.Tracer {
        return GetTracerProvider().Tracer(name, opts...)
}

func GetTracerProvider() trace.TracerProvider {
        return global.TracerProvider()
}

func SetTracerProvider(tp trace.TracerProvider) {
        global.SetTracerProvider(tp)
}

```

* **global**包的代码也很简单, 就是为了包装一层标准, 实际上是调用了**global**包
* 代码也没什么特别的, 只是使用了原子返回了一个默认的实例
* 最主要的就是`SetTracerProvider`方法, 它会通过`TracerProvider`拿到当前的实例(`gorm`,`redis`已经用的那个)
* 然后把当前要设置的`tp`传递给原来的的那个(且只会执行一次)

```go
package global // import "go.opentelemetry.io/otel/internal/global"

import (
        "sync"
        "sync/atomic"

        "go.opentelemetry.io/otel/metric"
        "go.opentelemetry.io/otel/propagation"
        "go.opentelemetry.io/otel/trace"
)

var (
        globalTracer        = defaultTracerValue()
        delegateTraceOnce             sync.Once
        delegateTextMapPropagatorOnce sync.Once
        delegateMeterOnce             sync.Once
)

type (
        tracerProviderHolder struct {
                tp trace.TracerProvider
        }

        propagatorsHolder struct {
                tm propagation.TextMapPropagator
        }

        meterProviderHolder struct {
                mp metric.MeterProvider
        }
)

func TracerProvider() trace.TracerProvider {
        return globalTracer.Load().(tracerProviderHolder).tp
}

func SetTracerProvider(tp trace.TracerProvider) {
        current := TracerProvider()

        if _, cOk := current.(*tracerProvider); cOk {
                if _, tpOk := tp.(*tracerProvider); tpOk && current == tp {
                        // Do not assign the default delegating TracerProvider to delegate
                        // to itself.
                        Error(
                                errors.New("no delegate configured in tracer provider"),
                                "Setting tracer provider to its current value. No delegate will be configured",
                        )
                        return
                }
        }

        delegateTraceOnce.Do(func() {
                if def, ok := current.(*tracerProvider); ok {
                        def.setDelegate(tp)
                }
        })
        globalTracer.Store(tracerProviderHolder{tp: tp})
}

func defaultTracerValue() *atomic.Value {
        v := &atomic.Value{}
        v.Store(tracerProviderHolder{tp: &tracerProvider{}})
        return v
}
```


## 图解


```text
  ┌───────────────────┐
  │                   │
  │                   │
  │  tracer.Start()   ├──────────────────────────────┐
  │  tracer.Tracer()  │                              │
  │                   │                              │
  │                   │               6. 实 际 是 使 用 delegate 去 调 用 对 应 的 方 法
  ├───────────────────┘                              │
  │                                                  │          zrpc.MustNewSerfer()
  │  go-redis/gorm/x                                 │                 │
  │   tracer = otel.GetTracerProvider()──┐           │                 │
  │                                      │           │                 │
  │                                      │           │        4. 设 置 链 路 追 踪 服 务 提 供 者
                           2. get global default     │                 │
                                         │           │                 │
┌───────────package global─────────────  │ ───────┐  │                 ▼
│                                        ▼        │  │        otel.SetTracerProvider()
│           ┌───────────────TracerProvider()      │  │                 │ tp = 0x03
│           │                                     │  │                 │
│           │                                     │  │                 │
│           │                                     │  │                 ▼
│   3. return global default                      │  │      ┌─ global.SetTracerProvider()
│           │                                     │  │      │          │
│           ▼                                     │  │      │          │
│  ┌─►globalTracer tracerProviderHolder = 0x01    │  │      │  5. 修改当前全局默认
│  │    tp tracerProvider = 0x02         ┌────┐   │  │      │          │
│  │      delegate trace.TracerProvider =│nil │   │  │      │          ▼
│  │                                     │    │   │  │      │ globalTracer tracerProviderHolder = 0x04
│  │                                     │    │      │      │   tp tracerProvider = 0x03
│ 1. init global default                 │0x03│◄─────┘      │
│  │                                     └────┘             │
│  └──defaultTracerValue()                 ▲      │         │
│                                          │      │         │
└────────────────────────────────────────  │ ─────┘         │
                                           │                │
                                           │                │
                                           │                │
                                           │                │
                                           │                │
                                           │       5-1. 把 delegate 从 nil => 0x03
                                           │                │
                                           │                │
                                           └────────────────┘
```

* 调试断点的值也能说明这一点

![]( https://i.imgur.com/JxUEzkX.png)
举报· 33 次点击
登录 注册 站外分享
快来抢沙发
0 条回复  
返回顶部