V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
seth19960929
V2EX  ›  Go 编程语言

Go 链路追踪中为什么先调用 otel.GetTracerProvider() 再调用 otel.SetTracerProvider() 也能上报成功?

  •  
  •   seth19960929 · 90 天前 · 1074 次点击
    这是一个创建于 90 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    package main
    
    import (
    	"fmt"
    	"github.com/zeromicro/go-zero/zrpc"
    )
    
    func main() {
    	
    	// 1. 获取服务依赖的配置
    	svcCtx := svc.NewServiceContext()
    	
    	// 2. 实例化服务但配置, 实际上这里的代码会调用 otel.SetTracerProvider()
    	// server.NewServer() => 
    	// service.SetUp => 
    	// trace.StartAgent => 
    	// trace.startAgent() => 
    	// otel.SetTracerProvider()
    	s := zrpc.MustNewServer()
    	defer s.Stop()
    
    
    	fmt.Printf("Starting rpc server 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/driver/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
    }
    

    代码追踪

    • otel包的代码也很简单, 就是为了包装一层标准, 实际上是调用了global
    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传递给原来的的那个(且只会执行一次)
    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
    }
    

    图解

      ┌───────────────────┐
      │                   │
      │                   │
      │  tracer.Start()   ├──────────────────────────────┐
      │  tracer.Tracer()  │                              │
      │                   │                              │
      │                   │               6. 实 际 是 使 用 delegate 去 调 用 对 应 的 方 法
      ├───────────────────┘                              │
      │                                                  │          zrpc.MustNewServer()
      │  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
                                               │                │
                                               │                │
                                               └────────────────┘
    
    • 调试断点的值也能说明这一点

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2800 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 11:46 · PVG 19:46 · LAX 03:46 · JFK 06:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.