go-gin-api 路由中間件 - Jaeger 鏈路追蹤

概述

首先同步下項目概況:



上篇文章分享了,路由中間件 - Jaeger 鏈路追蹤(理論篇)。

這篇文章我們分享:路由中間件 - Jaeger 鏈路追蹤(實戰篇)。

說實話,這篇文章確實讓你們久等了,主要是裏面有一些技術點都是剛剛研究的,沒有存貨。

先看下我們要實現的東西:


API 調用了 5 個服務,其中 4 個 gRPC 服務,1 個 HTTP 服務,服務與服務之間又相互調用:

    Speak 服務,又調用了 Listen 服務 和 Sing 服務。

    Read 服務,又調用了 Listen 服務 和 Sing 服務。

    Write 服務,又調用了 Listen 服務 和 Sing 服務。

我們要實現的就是查看 API 調用的鏈路。

關於一些理論的東西,你們能夠去看看上篇文章或查閱一些資料,這篇文章就是實現怎麼用。

OK,開整。

Jaeger 部署

我們使用 All in one 的方式,進行本地部署。

下載地址:https://www.jaegertracing.io/download/

個人電腦是 macOS 選擇 -> Binaries -> macOS

下載後並解壓,會發現如下文件:

php

    example-hotrod

    jaeger-agent

    jaeger-all-in-one

    jaeger-collector

    jaeger-ingester

    jaeger-query

 


進入到解壓後的目錄執行:git

./jaeger-all-in-one

 



目測啓動後,訪問地址:

http://127.0.0.1:16686/



到這,Jaeger 已經部署成功了。

準備測試服務

準備的五個測試服務以下:
聽(listen)

    端口:9901

    通信:gRPC

說(speak)

    端口:9902

    通信:gRPC

讀(read)

    端口:9903

    通信:gRPC

寫(write)

    端口:9904

    通信:gRPC

唱(sing)

    端口:9905

    通信:HTTP

聽、說、讀、寫、唱,想這幾個服務的名稱就花了很久 ~

我默認你們都會寫 grpc 服務,若是不會寫的,能夠查看下我原來的文章《Go gRPC Hello World》。

應用示例github

實例化 Tracer

    func NewJaegerTracer(serviceName string, jaegerHostPort string) (opentracing.Tracer, io.Closer, error) {    
        cfg := &jaegerConfig.Configuration {    
            Sampler: &jaegerConfig.SamplerConfig{    
                Type  : "const", //固定採樣    
                Param : 1,       //1=全採樣、0=不採樣    
            },    
            Reporter: &jaegerConfig.ReporterConfig{    
                LogSpans           : true,    
                LocalAgentHostPort : jaegerHostPort,    
            },    
            ServiceName: serviceName,    
        }    
        tracer, closer, err := cfg.NewTracer(jaegerConfig.Logger(jaeger.StdLogger))    
        if err != nil {    
            panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err))    
        }    
        opentracing.SetGlobalTracer(tracer)    
        return tracer, closer, err    
    }


HTTP 注入

    injectErr := jaeger.Tracer.Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))    
    if injectErr != nil {    
        log.Fatalf("%s: Couldn't inject headers", err)    
    }


HTTP 攔截

    spCtx, err := opentracing.GlobalTracer().Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(c.Request.Header))    
    if err != nil {    
        ParentSpan = Tracer.StartSpan(c.Request.URL.Path)    
        defer ParentSpan.Finish()    
    } else {    
        ParentSpan = opentracing.StartSpan(    
            c.Request.URL.Path,    
            opentracing.ChildOf(spCtx),    
            opentracing.Tag{Key: string(ext.Component), Value: "HTTP"},    
            ext.SpanKindRPCServer,    
        )    
        defer ParentSpan.Finish()    
    }


gRPC 注入

    func ClientInterceptor(tracer opentracing.Tracer, spanContext opentracing.SpanContext) grpc.UnaryClientInterceptor {    
        return func(ctx context.Context, method string,    
            req, reply interface{}, cc *grpc.ClientConn,    
            invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {    
            span := opentracing.StartSpan(    
                "call gRPC",    
                opentracing.ChildOf(spanContext),    
                opentracing.Tag{Key: string(ext.Component), Value: "gRPC"},    
                ext.SpanKindRPCClient,    
            )    
            defer span.Finish()    
            md, ok := metadata.FromOutgoingContext(ctx)    
            if !ok {    
                md = metadata.New(nil)    
            } else {    
                md = md.Copy()    
            }    
            err := tracer.Inject(span.Context(), opentracing.TextMap, MDReaderWriter{md})    
            if err != nil {    
                span.LogFields(log.String("inject-error", err.Error()))    
            }    
            newCtx := metadata.NewOutgoingContext(ctx, md)    
            err = invoker(newCtx, method, req, reply, cc, opts...)    
            if err != nil {    
                span.LogFields(log.String("call-error", err.Error()))    
            }    
            return err    
        }    
    }


gRPC 攔截

    func serverInterceptor(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)    
            } else {    
                span := tracer.StartSpan(    
                    info.FullMethod,    
                    ext.RPCServerOption(spanContext),    
                    opentracing.Tag{Key: string(ext.Component), Value: "gRPC"},    
                    ext.SpanKindRPCServer,    
                )    
                defer span.Finish()    
                ParentContext = opentracing.ContextWithSpan(ctx, span)    
            }    
            return handler(ParentContext, req)    
        }    
    }

 


上面是一些核心的代碼,涉及到的所有代碼我都會上傳到 github,供下載。

運行
啓動服務api

 // 啓動 Listen 服務    
    cd listen && go run main.go    
    // 啓動 Speak 服務    
    cd speak && go run main.go    
    // 啓動 Read 服務    
    cd read && go run main.go    
    // 啓動 Write 服務    
    cd write && go run main.go    
    // 啓動 Sing 服務    
    cd sing && go run main.go    
    // 啓動 go-gin-api 服務    
    cd go-gin-api && go run main.go

 


訪問路由

測試

http://127.0.0.1:9999/jaeger_test

 



效果




基本實現了,就到這吧。

spa

相關文章
相關標籤/搜索