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
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)
}
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
│ │
│ │
└────────────────┘
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.