基于grpc構(gòu)建微服務(wù)框架-集成

go基于grpc構(gòu)建微服務(wù)框架-集成opentracing

1.概述

存在這樣一種場(chǎng)景凿掂,當(dāng)我們進(jìn)行微服務(wù)拆分后反浓,一個(gè)請(qǐng)求將會(huì)經(jīng)過(guò)多個(gè)服務(wù)處理之后再返回,這時(shí),如果在請(qǐng)求的鏈路上某個(gè)服務(wù)出現(xiàn)故障時(shí)豌汇,排查故障將會(huì)比較困難.
我們可能需要將請(qǐng)求經(jīng)過(guò)的服務(wù),挨個(gè)查看日志進(jìn)行分析充岛,當(dāng)服務(wù)有幾十上百個(gè)實(shí)例時(shí)保檐,這無(wú)疑是可怕的.因此為了解決這種問(wèn)題,調(diào)用鏈追蹤應(yīng)運(yùn)而生.

2.opentracing

1.1 opentracing作用

調(diào)用鏈追蹤最先由googel在Dapper這篇論文中提出崔梗,OpenTracing主要定義了相關(guān)的協(xié)議以及接口夜只,這樣各個(gè)語(yǔ)言只要按照Opentracing的接口以及協(xié)議實(shí)現(xiàn)數(shù)據(jù)上報(bào),那么調(diào)用信息就能統(tǒng)一被收集.

image.png

如上圖所示蒜魄,接口可能首先經(jīng)過(guò)web框架扔亥,然后調(diào)用auth服務(wù)场躯,通過(guò)調(diào)用鏈,將請(qǐng)求經(jīng)過(guò)的服務(wù)進(jìn)行編號(hào)旅挤,統(tǒng)一收集起來(lái)踢关,形成邏輯上的鏈路,這樣粘茄,我們就可以看到請(qǐng)求經(jīng)過(guò)了哪些服務(wù)签舞,從而形成服務(wù)依賴(lài)的拓?fù)洌?/p>

image.png

如上,總鏈路由每段鏈路組成柒瓣,每段鏈路均代表經(jīng)過(guò)的服務(wù)儒搭,耗時(shí)可用于分析系統(tǒng)瓶頸,當(dāng)某個(gè)請(qǐng)求返回較慢時(shí)芙贫,可以通過(guò)排查某一段鏈路的耗時(shí)情況搂鲫,從而分析是哪個(gè)服務(wù)出現(xiàn)延時(shí)較高,今個(gè)到具體的服務(wù)中分析具體的問(wèn)題.

1.2 opentraing關(guān)鍵術(shù)語(yǔ)

  • Traces(調(diào)用鏈)

一次調(diào)用的鏈路磺平,由TraceID唯一標(biāo)志魂仍,如一次請(qǐng)求則通常為一個(gè)trace,trace由所有途徑的span組成.

  • Spans(調(diào)用跨度)

沒(méi)進(jìn)過(guò)一個(gè)服務(wù)則將span,同樣每個(gè)span由spanID唯一標(biāo)志.

  • Span Tags(跨度標(biāo)簽)

span的標(biāo)簽褪秀,如一段span是調(diào)用redis的蓄诽,而可以設(shè)置redis的標(biāo)簽,這樣通過(guò)搜索redis關(guān)鍵字媒吗,我們就可以查詢(xún)出所有相關(guān)的span以及trace.

  • Baggage Item(附帶數(shù)據(jù))

附加的數(shù)據(jù),由key:value組成仑氛,通過(guò)附加數(shù)據(jù),可以給調(diào)用鏈更多的描述信息闸英,不過(guò)考慮到傳輸問(wèn)題锯岖,附加數(shù)據(jù)應(yīng)該盡可能少.

1.3 jaeger & zipkin

目前開(kāi)源的實(shí)現(xiàn)有zipkin以及jaeger

  • zipkin

zipkin主要由java編寫(xiě),通過(guò)各個(gè)語(yǔ)言的上報(bào)庫(kù)實(shí)現(xiàn)將數(shù)據(jù)上報(bào)到collector,collector再將數(shù)據(jù)存儲(chǔ)甫何,并通過(guò)API提供給前段UI展示.

image.png
  • jaeger

jaeger由go實(shí)現(xiàn)出吹,由uber開(kāi)發(fā),目前是cloud native項(xiàng)目,流程與zipkin類(lèi)似辙喂,增加jager-agent這樣個(gè)組件捶牢,這個(gè)組件官方建議是每個(gè)機(jī)器都部署一個(gè),通過(guò)這個(gè)組件再將數(shù)據(jù)上報(bào)到collector存儲(chǔ)展示巍耗,另外秋麸,里面做了對(duì)zipkin的適配,其實(shí)一開(kāi)始他們用的也是zipkin炬太,為毛后面要自己造輪子灸蟆?見(jiàn)他們的解釋. 鏈接

image.png

總的來(lái)說(shuō)兩者都能基本滿足opentracing的功能,具體的選擇可以結(jié)合自身技術(shù)棧和癖好.

2. grpc集成opentracing

grpc集成opentracing并不難亲族,因?yàn)間rpc服務(wù)端以及調(diào)用端分別聲明了UnaryClientInterceptor以及UnaryServerInterceptor兩個(gè)回調(diào)函數(shù)炒考,因此只需要重寫(xiě)這兩個(gè)回調(diào)函數(shù)可缚,并在重寫(xiě)的回調(diào)函數(shù)中調(diào)用opentracing接口進(jìn)行上報(bào)即可.
初始化時(shí)傳入重寫(xiě)后的回調(diào)函數(shù),同時(shí)二選一初始化jager或者zipkin斋枢,然后你就可以開(kāi)啟分布式調(diào)用鏈追蹤之旅了.

完整的代碼見(jiàn)grpc-wrapper

2.1 client端

// OpenTracingClientInterceptor  rewrite client's interceptor with open tracing
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,如果上層沒(méi)有開(kāi)啟追蹤帘靡,則這里新建一個(gè)
        //追蹤,如果上層已經(jīng)有了杏慰,測(cè)創(chuàng)建子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數(shù)據(jù)取出测柠,如果沒(méi)有則新建一個(gè)metadata
        md, ok := metadata.FromOutgoingContext(ctx)
        if !ok {
            md = metadata.New(nil)
        } else {
            md = md.Copy()
        }
        mdWriter := MDReaderWriter{md}

        //將追蹤數(shù)據(jù)注入到metadata中
        err := tracer.Inject(cliSpan.Context(), opentracing.TextMap, mdWriter)
        if err != nil {
            grpclog.Errorf("inject to metadata err %v", err)
        }
        //將metadata數(shù)據(jù)裝入context中
        ctx = metadata.NewOutgoingContext(ctx, md)
        //使用帶有追蹤數(shù)據(jù)的context進(jìn)行g(shù)rpc調(diào)用.
        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 tracing
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中取出最終數(shù)據(jù),并創(chuàng)建出span對(duì)象
        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傳入應(yīng)用代碼中進(jìn)行調(diào)用
        return handler(ctx, req)
    }
}

由于opentracing定義了相關(guān)的接口缘滥,而jaeger以及zipkin進(jìn)行了相應(yīng)的實(shí)現(xiàn)轰胁,因此這里可以使用jaeger的也可以使用zipkin進(jìn)行上報(bào).

3.效果

jaeger服務(wù)主頁(yè)信息

image.png

每條調(diào)用鏈信息

image.png

4.參考

zipkin
jaeger
OpenTracing
grpc-wrapper

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市朝扼,隨后出現(xiàn)的幾起案子赃阀,更是在濱河造成了極大的恐慌,老刑警劉巖擎颖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件榛斯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡搂捧,警方通過(guò)查閱死者的電腦和手機(jī)驮俗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)允跑,“玉大人王凑,你說(shuō)我怎么就攤上這事×浚” “怎么了索烹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)弱睦。 經(jīng)常有香客問(wèn)我百姓,道長(zhǎng),這世上最難降的妖魔是什么况木? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任垒拢,我火速辦了婚禮,結(jié)果婚禮上火惊,老公的妹妹穿的比我還像新娘子库。我一直安慰自己,他們只是感情好矗晃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著宴倍,像睡著了一般张症。 火紅的嫁衣襯著肌膚如雪仓技。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天俗他,我揣著相機(jī)與錄音脖捻,去河邊找鬼。 笑死兆衅,一個(gè)胖子當(dāng)著我的面吹牛地沮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播羡亩,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼摩疑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了畏铆?” 一聲冷哼從身側(cè)響起雷袋,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎辞居,沒(méi)想到半個(gè)月后楷怒,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瓦灶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年鸠删,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贼陶。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡刃泡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出每界,到底是詐尸還是另有隱情捅僵,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布眨层,位于F島的核電站庙楚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏趴樱。R本人自食惡果不足惜馒闷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望叁征。 院中可真熱鬧纳账,春花似錦、人聲如沸捺疼。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至卧秘,卻和暖如春呢袱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背翅敌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工羞福, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蚯涮。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓治专,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親遭顶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子张峰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容