go基於grpc構建微服務架構-整合opentracing

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

1.概述

存在這樣一種情境,當我們進行微服務拆分後,一個請求將會經過多個服務處理之後再返回,這時,如果在請求的鏈路上某個服務出現故障時,排查故障將會比較困難.
我們可能需要將請求經過的服務,挨個查看日誌進行分析,當服務有幾十上百個執行個體時,這無疑是可怕的.因此為瞭解決這種問題,調用鏈追蹤應運而生.

2.opentracing

1.1 opentracing作用

調用鏈追蹤最先由googel在Dapper這篇論文中提出,OpenTracing主要定義了相關的協議以及介面,這樣各個語言只要按照Opentracing的介面以及協議實現資料上報,那麼調用資訊就能統一被收集.

如所示,介面可能首先經過web架構,然後調用auth服務,通過調用鏈,將請求經過的服務進行編號,統一收集起來,形成邏輯上的鏈路,這樣,我們就可以看到請求經過了哪些服務,從而形成服務依賴的拓撲.

如上,總鏈路由每段鏈路組成,每段鏈路均代表經過的服務,耗時可用於分析系統瓶頸,當某個請求返回較慢時,可以通過排查某一段鏈路的耗時情況,從而分析是哪個服務出現延時較高,今個到具體的服務中分析具體的問題.

1.2 opentraing關鍵術語

  • Traces(調用鏈)

一次調用的鏈路,由TraceID唯一標誌,如一次請求則通常為一個trace,trace由所有途徑的span組成.

  • Spans(調用跨度)

沒進過一個服務則將span,同樣每個span由spanID唯一標誌.

  • Span Tags(跨度標籤)

span的標籤,如一段span是調用redis的,而可以設定redis的標籤,這樣通過搜尋redis關鍵字,我們就可以查詢出所有相關的span以及trace.

  • Baggage Item(附帶資料)

附加的資料,由key:value組成,通過附加資料,可以給調用鏈更多的描述資訊,不過考慮到傳輸問題,附加資料應該儘可能少.

1.3 jaeger & zipkin

目前開源的實現有zipkin以及jaeger

  • zipkin

zipkin主要由java編寫,通過各個語言的上報庫實現將資料上報到collector,collector再將資料存放區,並通過API提供給前段UI展示.

  • jaeger

jaeger由go實現,由uber開發,目前是cloud native項目,流程與zipkin類似,增加jager-agent這樣個組件,這個組件官方建議是每個機器都部署一個,通過這個組件再將資料上報到collector儲存展示,另外,裡面做了對zipkin的適配,其實一開始他們用的也是zipkin,為毛後面要自己造輪子?見他們的解釋. 連結

總的來說兩者都能基本滿足opentracing的功能,具體的選擇可以結合自身技術棧和癖好.

2. grpc整合opentracing

grpc整合opentracing並不難,因為grpc服務端以及調用端分別聲明了UnaryClientInterceptor以及UnaryServerInterceptor兩個回呼函數,因此只需要重寫這兩個回呼函數,並在重寫的回呼函數中調用opentracing介面進行上報即可.
初始化時傳入重寫後的回呼函數,同時二選一初始化jager或者zipkin,然後你就可以開啟分布式調用鏈追蹤之旅了.

完整的代碼見grpc-wrapper

2.1 client端

// OpenTracingClientInterceptor  rewrite client's interceptor with open tracingfunc OpenTracingClientInterceptor(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 {        //從context中擷取spanContext,如果上層沒有開啟追蹤,則這裡建立一個        //追蹤,如果上層已經有了,測建立子span.        var parentCtx opentracing.SpanContext        if parent := opentracing.SpanFromContext(ctx); parent != nil {            parentCtx = parent.Context()        }        cliSpan := tracer.StartSpan(            method,            opentracing.ChildOf(parentCtx),            wrapper.TracingComponentTag,            ext.SpanKindRPCClient,        )        defer cliSpan.Finish()        //將之前放入context中的metadata資料取出,如果沒有則建立一個metadata        md, ok := metadata.FromOutgoingContext(ctx)        if !ok {            md = metadata.New(nil)        } else {            md = md.Copy()        }        mdWriter := MDReaderWriter{md}        //將追蹤資料注入到metadata中        err := tracer.Inject(cliSpan.Context(), opentracing.TextMap, mdWriter)        if err != nil {            grpclog.Errorf("inject to metadata err %v", err)        }        //將metadata資料裝入context中        ctx = metadata.NewOutgoingContext(ctx, md)        //使用帶有追蹤資料的context進行grpc調用.        err = invoker(ctx, method, req, resp, cc, opts...)        if err != nil {            cliSpan.LogFields(log.String("err", err.Error()))        }        return err    }}

2.2 server端

//OpentracingServerInterceptor rewrite server's interceptor with open tracingfunc OpentracingServerInterceptor(tracer opentracing.Tracer) grpc.UnaryServerInterceptor {    return func(        ctx context.Context,        req interface{},        info *grpc.UnaryServerInfo,        handler grpc.UnaryHandler,    ) (resp interface{}, err error) {                //從context中取出metadata        md, ok := metadata.FromIncomingContext(ctx)        if !ok {            md = metadata.New(nil)        }               //從metadata中取出最終資料,並建立出span對象        spanContext, err := tracer.Extract(opentracing.TextMap, MDReaderWriter{md})        if err != nil && err != opentracing.ErrSpanContextNotFound {            grpclog.Errorf("extract from metadata err %v", err)        }                //初始化server 端的span        serverSpan := tracer.StartSpan(            info.FullMethod,            ext.RPCServerOption(spanContext),            wrapper.TracingComponentTag,            ext.SpanKindRPCServer,        )        defer serverSpan.Finish()        ctx = opentracing.ContextWithSpan(ctx, serverSpan)             //將帶有追蹤的context傳入應用代碼中進行調用        return handler(ctx, req)    }}

由於opentracing定義了相關的介面,而jaeger以及zipkin進行了相應的實現,因此這裡可以使用jaeger的也可以使用zipkin進行上報.

3.效果

jaeger服務首頁資訊

每條調用鏈資訊

4.參考

zipkin
jaeger
OpenTracing
grpc-wrapper

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.