go-kit 微服務 服務鏈路追蹤 (jaeger 實現)(1)

hwholiday發表於2020-02-12

go-kit 微服務 服務鏈路追蹤(jaeger 實現)(1)

  • 對 grpc 呼叫新增鏈路追蹤

部署 jaeger

  • 生產環境部署
  • 本地測試

    • 可以直接用 Jaeger 的 all-in-one

      sudo docker pull jaegertracing/all-in-one
      sudo docker run -d -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -p5775:5775/udp -p6831:6831/udp -p6832:6832/udp -p5778:5778 -p16686:16686 -p14268:14268 -p9411:9411 jaegertracing/all-in-one:latest
      
    • 能正常訪問 http://127.0.0.1:16686/ 則安裝成功

編寫 jaeger 關於 grpc 程式碼

初始化客戶端

func NewJaegerTracer(serviceName string) (tracer opentracing.Tracer, closer io.Closer, err error) {
    cfg := &jaegerConfig.Configuration{
        Sampler: &jaegerConfig.SamplerConfig{
            Type:  "const", //固定取樣
            Param: 1,       //1=全取樣、0=不取樣
        },
        Reporter: &jaegerConfig.ReporterConfig{
            LogSpans:           true,
            LocalAgentHostPort: "127.0.0.1:6831",
        },
        ServiceName: serviceName,
    }
    tracer, closer, err = cfg.NewTracer(jaegerConfig.Logger(jaeger.StdLogger))
    if err != nil {
        return
    }
    opentracing.SetGlobalTracer(tracer)
    return
}

grpc 客戶端中介軟體

func JaegerClientMiddleware(tracer opentracing.Tracer) grpc.UnaryClientInterceptor {
    return func(
        ctx context.Context,
        method string,
        req, resp interface{},
        cc *grpc.ClientConn,
        invoker grpc.UnaryInvoker,
        opts ...grpc.CallOption,
    ) error {
        var parentCtx opentracing.SpanContext
        //先判斷ctx裡面有沒有 span 資訊
        //沒有就生成一個
        if parent := opentracing.SpanFromContext(ctx); parent != nil {
            parentCtx = parent.Context()
        }
        cliSpan := tracer.StartSpan(
            method,
            opentracing.ChildOf(parentCtx),//父子關係的span關係
            TracingComponentTag,//grcp tag
            ext.SpanKindRPCClient,//客戶端 tag
        )
        defer cliSpan.Finish()
        //從context中獲取metadata。md.(type) == map[string][]string
        md, ok := metadata.FromOutgoingContext(ctx)
        if !ok {
            md = metadata.New(nil)
        } else {
            ////如果對metadata進行修改,那麼需要用拷貝的副本進行修改。
            md = md.Copy()
        }
        //定義一個carrier,下面的Inject注入資料需要用到。carrier.(type) == map[string]string
        //carrier := opentracing.TextMapCarrier{}
        mdWriter := MDReaderWriter{md}
        ////將span的context資訊注入到carrier中
        err := tracer.Inject(cliSpan.Context(), opentracing.TextMap, mdWriter)
        if err != nil {
            grpclog.Errorf("inject to metadata err %v", err)
        }
        ////建立一個新的context,把metadata附帶上
        ctx = metadata.NewOutgoingContext(ctx, md)
        err = invoker(ctx, method, req, resp, cc, opts...)
        if err != nil {
            cliSpan.LogFields(log.String("err", err.Error()))
        }
        return err
    }
}

grpc 服務端中介軟體

func JaegerServerMiddleware(tracer opentracing.Tracer) grpc.UnaryServerInterceptor {
    return func(
        ctx context.Context,
        req interface{},
        info *grpc.UnaryServerInfo,
        handler grpc.UnaryHandler,
    ) (resp interface{}, err error) {
        md, ok := metadata.FromIncomingContext(ctx)
        if !ok {
            md = metadata.New(nil)
        }
        spanContext, err := tracer.Extract(opentracing.TextMap, MDReaderWriter{md})
        if err != nil && err != opentracing.ErrSpanContextNotFound {
            grpclog.Errorf("extract from metadata err %v", err)
        }
        serverSpan := tracer.StartSpan(
            info.FullMethod,
            ext.RPCServerOption(spanContext),
            TracingComponentTag,
            ext.SpanKindRPCServer,
        )
        defer serverSpan.Finish()
        ctx = opentracing.ContextWithSpan(ctx, serverSpan)
        return handler(ctx, req)
    }
}

修改服務端方法

......
tracer, _, err := utils.NewJaegerTracer("user_agent_server")
if err != nil {
    utils.GetLogger().Warn("[user_agent] NewJaegerTracer", zap.Error(err))
    quitChan <- err
}
Registar.Register()
utils.GetLogger().Info("[user_agent] grpc run " + *grpcAddr)
chainUnaryServer := grpcmiddleware.ChainUnaryServer(
    grpctransport.Interceptor,
    tils.JaegerServerMiddleware(tracer),
)
baseServer := grpc.NewServer(grpc.UnaryInterceptor(chainUnaryServer))
pb.RegisterUserServer(baseServer, grpcServer)
quitChan <- baseServer.Serve(grpcListener)
......

修改客戶端程式碼

......
tracer, _, err := utils.NewJaegerTracer("user_agent_client")
if err != nil {
    return nil, err
}
......


......
conn, err := grpc.Dial(instance, grpc.WithInsecure(),
        grpc.WithUnaryInterceptor(utils.JaegerClientMiddleware(u.tracer)), )
if err != nil {
    return nil, nil, err
}
srv := u.NewGRPCClient(conn)
......

執行

結語

  • 通過後臺介面我們可以看到請求資訊,以便與我們對服務的瞭解
  • jaeger 的用法還有很多,這裡只展示簡單的使用,更加高階的功能歡迎大家一起討論
  • 歡迎新增 QQ 一起討論

完整程式碼地址

參考文獻

聯絡 QQ: 3355168235

更多原創文章乾貨分享,請關注公眾號
  • go-kit 微服務 服務鏈路追蹤 (jaeger 實現)(1)
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章