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

g4zhuj發表於2018-04-23

1.概述

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

2.opentracing

1.1 opentracing作用

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

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

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

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

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

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展示.

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

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

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

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

2. grpc整合opentracing

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

完整的程式碼見grpc-wrapper

2.1 client端

func 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端

func 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服務主頁資訊

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

每條呼叫鏈資訊

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

4.參考

zipkin
jaeger
OpenTracing
grpc-wrapper

相關文章