最近在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
│ │
│ │
└────────────────┘
- 调试断点的值也能说明这一点
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。