小天管理 发表于 2024年9月23日 发表于 2024年9月23日 最近在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 } 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 按理说肯定要先Set才能Get,而实际上otel采用了委托的方式让我们可以先get然后再set 代码追踪 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 │ │ │ │ └────────────────┘ 调试断点的值也能说明这一点
已推荐帖子