<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          微服務(wù)-如何解決鏈路追蹤問題

          共 12086字,需瀏覽 25分鐘

           ·

          2020-11-12 15:48

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          ? 作者?|??王澤賓

          來源 |? urlify.cn/6jaMbq

          66套java從入門到精通實(shí)戰(zhàn)課程分享

          一、鏈路追蹤

          微服務(wù)架構(gòu)是將單個(gè)應(yīng)用程序被劃分成各種小而連接的服務(wù),每一個(gè)服務(wù)完成一個(gè)單一的業(yè)務(wù)功能,相互之間保持獨(dú)立和解耦,每個(gè)服務(wù)都可以獨(dú)立演進(jìn)。相對(duì)于傳統(tǒng)的單體服務(wù),微服務(wù)具有隔離性、技術(shù)異構(gòu)性、可擴(kuò)展性以及簡(jiǎn)化部署等優(yōu)點(diǎn)。

          同樣的,微服務(wù)架構(gòu)在帶來諸多益處的同時(shí),也為系統(tǒng)增加了不少?gòu)?fù)雜性。它作為一種分布式服務(wù),通常部署于由不同的數(shù)據(jù)中心、不同的服務(wù)器組成的集群上。而且,同一個(gè)微服務(wù)系統(tǒng)可能是由不同的團(tuán)隊(duì)、不同的語言開發(fā)而成。通常一個(gè)應(yīng)用由多個(gè)微服務(wù)組成,微服務(wù)之間的數(shù)據(jù)交互需要通過遠(yuǎn)過程調(diào)用的方式完成,所以在一個(gè)由眾多微服務(wù)構(gòu)成的系統(tǒng)中,請(qǐng)求需要在各服務(wù)之間流轉(zhuǎn),調(diào)用鏈路錯(cuò)綜復(fù)雜,一旦出現(xiàn)問題,是很難進(jìn)行問題定位和追查異常的。

          鏈路追蹤系統(tǒng)就是為解決上述問題而產(chǎn)生的,它用來追蹤每一個(gè)請(qǐng)求的完整調(diào)用鏈路,記錄從請(qǐng)求開始到請(qǐng)求結(jié)束期間調(diào)用的任務(wù)名稱、耗時(shí)、標(biāo)簽數(shù)據(jù)以及日志信息,并通過可視化的界面進(jìn)行分析和展示,來幫助技術(shù)人員準(zhǔn)確地定位異常服務(wù)、發(fā)現(xiàn)性能瓶頸、梳理調(diào)用鏈路以及預(yù)估系統(tǒng)容量。

          鏈路追蹤系統(tǒng)的理論模型幾乎都借鑒了 Google 的一篇論文”Dapper, a Large-Scale Distributed Systems Tracing Infrastructure”,典型產(chǎn)品有Uber jaeger、Twitter zipkin、淘寶鷹眼等。這些產(chǎn)品的實(shí)現(xiàn)方式雖然不盡相同,但核心步驟一般都有三個(gè):數(shù)據(jù)采集、數(shù)據(jù)存儲(chǔ)和查詢展示。

          鏈路追蹤系統(tǒng)第一步,也是最基本的工作就是數(shù)據(jù)采集。在這個(gè)過程中,鏈路追蹤系統(tǒng)需要侵入用戶代碼進(jìn)行埋點(diǎn),用于收集追蹤數(shù)據(jù)。但是由于不同的鏈路追蹤系統(tǒng)的API互不兼容,所以埋點(diǎn)代碼寫法各異,導(dǎo)致用戶在切換不同鏈路追蹤產(chǎn)品時(shí)需要做很大的改動(dòng)。為了解決這類問題,于是誕生了OpenTracing規(guī)范,旨在統(tǒng)一鏈路追蹤系統(tǒng)的API。

          二、OpenTracing規(guī)范

          OpenTracing 是一套分布式追蹤協(xié)議,與平臺(tái)和語言無關(guān),具有統(tǒng)一的接口規(guī)范,方便接入不同的分布式追蹤系統(tǒng)。

          OpenTracing語義規(guī)范詳見:https://github.com/opentracing/specification/blob/master/specification.md

          2.1 數(shù)據(jù)模型(Data Model)

          OpenTracing語義規(guī)范中定義的數(shù)據(jù)模型有 Trace、Sapn以及Reference。

          2.1.1 Trace

          Trace表示一條完整的追蹤鏈路,例如:一個(gè)事務(wù)或者一個(gè)流程的執(zhí)行過程。一個(gè) Trace 是由一個(gè)或者多個(gè) Span 組成的有向無環(huán)圖(DAG)。

          下圖表示一個(gè)由8個(gè)Span組成的Trace:

                  [Span A]  ←←←(the root span)
          |
          +------+------+
          | |
          [Span B] [Span C] ←←←(Span C is a `ChildOf` Span A)
          | |
          [Span D] +---+-------+
          | |
          [Span E] [Span F] >>> [Span G] >>> [Span H]



          (Span G `FollowsFrom` Span F)

          按照時(shí)間軸方式更為直觀地展現(xiàn)該Trace:

          ––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time

          [Span A···················································]
          [Span B··············································]
          [Span D··········································]
          [Span C········································]
          [Span E·······] [Span F··] [Span G··] [Span H··]
          2.1.2 Span

          Span表示一個(gè)獨(dú)立的工作單元,它是一條追蹤鏈路的基本組成要素。例如:一次RPC調(diào)用、一次函數(shù)調(diào)用或者一次Http請(qǐng)求。

          每個(gè)Span封裝了如下狀態(tài):

          • 操作名稱

            用于表示該Span的任務(wù)名稱。例如:一個(gè) RPC方法名, 一個(gè)函數(shù)名,或者大型任務(wù)中的子任務(wù)名稱。

          • 開始時(shí)間戳

            任務(wù)開始時(shí)間。

          • 結(jié)束時(shí)間戳。

            任務(wù)結(jié)束時(shí)間。通過Span的結(jié)束時(shí)間戳和開始時(shí)間戳,就能夠計(jì)算出該Span的整體耗時(shí)。

          • 一組Span標(biāo)簽

            每一個(gè)Span標(biāo)簽是一個(gè)鍵值對(duì)。鍵必須是字符串,值可以是字符串、布爾或數(shù)值類型。常見標(biāo)簽鍵可參考:https://github.com/opentracing/specification/blob/master/semantic_conventions.md

          • 一組Span日志

            每一條Span日志由一個(gè)鍵值對(duì)和一個(gè)相應(yīng)的時(shí)間戳組成。鍵必須是字符串,值可以是任何類型。常見日志鍵參考:https://github.com/opentracing/specification/blob/master/semantic_conventions.md

          2.1.3 Reference

          一個(gè)Span可以與一個(gè)或者多個(gè)Span存在因果關(guān)系,這種關(guān)系稱為Reference。OpenTracing目前定義了兩種關(guān)系:ChildOf(父子)關(guān)系 和 FollowsFrom(跟隨)關(guān)系。

          • ChildOf關(guān)系

            父Span的執(zhí)行依賴子Span的執(zhí)行結(jié)果,此時(shí)子Span對(duì)父Span的Reference關(guān)系是ChildOf。比如對(duì)于一次RPC調(diào)用,服務(wù)端的Span(子Span)與客戶端調(diào)用的Span(父Span)就是ChildOf關(guān)系。

          • FollowsFrom關(guān)系

            父Span的執(zhí)行不依賴子Span的執(zhí)行結(jié)果,此時(shí)子Span對(duì)父Span的Reference關(guān)系是FollowFrom。FollowFrom常用于表示異步調(diào)用,例如消息隊(duì)列中Consumer Span與Producer Span之間的關(guān)系。

          2.2 應(yīng)用接口(API)

          2.2.1 Tracer

          Tracer接口用于創(chuàng)建Span、跨進(jìn)程注入數(shù)據(jù)和提取數(shù)據(jù)。通常具有以下功能:

          • Start a new span
            創(chuàng)建并啟動(dòng)一個(gè)新的Span。

          • Inject
            將SpanContext注入載體(Carrier)。

          • Extract
            從載體(Carrier)中提取SpanContext。

          2.2.2 Span
          • Retrieve a SpanContext
            返回Span對(duì)應(yīng)的SpanContext。

          • Overwrite the operation name
            更新操作名稱。

          • Set a span tag
            設(shè)置Span標(biāo)簽數(shù)據(jù)。

          • Log structured data
            記錄結(jié)構(gòu)化數(shù)據(jù)。

          • Set a baggage item
            baggage item是字符串型的鍵值對(duì),它對(duì)應(yīng)于某個(gè) Span,隨Trace一起傳播。由于每個(gè)鍵值都會(huì)被拷貝到每一個(gè)本地及遠(yuǎn)程的子Span,這可能導(dǎo)致巨大的網(wǎng)絡(luò)和CPU開銷。

          • Get a baggage item
            獲取baggage item的值。

          • Finish
            結(jié)束一個(gè)Span。

          2.2.3 Span Context

          用于攜帶跨越服務(wù)邊界的數(shù)據(jù),包括trace ID、Span ID以及需要傳播到下游Span的baggage數(shù)據(jù)。在OpenTracing中,強(qiáng)制要求SpanContext實(shí)例不可變,以避免在Span完成和引用時(shí)出現(xiàn)復(fù)雜的生命周期問題。

          2.2.4 NoopTracer

          所有對(duì)OpenTracing API的實(shí)現(xiàn),必須提供某種形式的NoopTracer,用于標(biāo)記控制OpenTracing或注入對(duì)測(cè)試無害的東西。

          三、Jaeger

          Jaeger是Uber開源的分布式追蹤系統(tǒng),它的應(yīng)用接口完全遵循OpenTracing規(guī)范。jaeger本身采用go語言編寫,具有跨平臺(tái)跨語言的特性,提供了各種語言的客戶端調(diào)用接口,例如c++、java、go、python、ruby、php、nodejs等。項(xiàng)目地址:https://github.com/jaegertracing

          3.1 Jaeger組件

          [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-miLIEWHv-1604561903414)(https://i.loli.net/2020/04/13/bvTxdUkBRuawY1F.png)]

          • jaeger-client

            jaeger的客戶端代碼庫(kù),它實(shí)現(xiàn)了OpenTracing協(xié)議。當(dāng)我們的應(yīng)用程序?qū)⑵溲b配后,負(fù)責(zé)收集數(shù)據(jù),并發(fā)送到j(luò)aeger-agent。這是我們唯一需要編寫代碼的地方。

          • jaeger-agent

            負(fù)責(zé)接收從jaeger-client發(fā)來的Trace/Span信息,并批量上傳到j(luò)aeger-collector。

          • jaeger-collector

            負(fù)責(zé)接收從jaeger-agent發(fā)來的Trace/Span信息,并經(jīng)過校驗(yàn)、索引等處理,然后寫入到后端存儲(chǔ)。

          • data store

            負(fù)責(zé)數(shù)據(jù)存儲(chǔ)。Jaeger的數(shù)據(jù)存儲(chǔ)是一個(gè)可插拔的組件,目前支持Cassandra、ElasticSearch和Kafka。

          • jaeger-query & ui

            負(fù)責(zé)數(shù)據(jù)查詢,并通過前端界面展示查詢結(jié)果。

            [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-ogkrm3Hb-1604561903417)(https://i.loli.net/2020/04/13/UMoHYtlX1ydsx5Q.jpg)]

          3.2 快速入門

          Jaeger官方提供了all-in-one鏡像,方便快速進(jìn)行測(cè)試:

          #?拉取鏡像
          $docker?pull?jaegertracing/all-in-one:latest

          #?運(yùn)行鏡像
          $docker?run?-d?--name?jaeger?\
          ??-e?COLLECTOR_ZIPKIN_HTTP_PORT=9411?\
          ??-p?5775:5775/udp?\
          ??-p?6831:6831/udp?\
          ??-p?6832:6832/udp?\
          ??-p?5778:5778?\
          ??-p?14268:14268?\
          ??-p?9411:9411?\
          ??-p?16686:16686?\
          ??jaegertracing/all-in-one:latest

          通過all-in-one鏡像啟動(dòng),我們發(fā)現(xiàn)Jaeger占據(jù)了很多端口。以下是端口使用說明:

          端口協(xié)議所屬模塊功能
          5775UDPagent接收壓縮格式的Zipkin thrift數(shù)據(jù)
          6831UDPagent接收壓縮格式的Jaeger thrift數(shù)據(jù)
          6832UDPagent接收二進(jìn)制格式的Jaeger thrift數(shù)據(jù)
          5778HTTPagent服務(wù)配置、采樣策略端口
          14268HTTPcollector接收由客戶端直接發(fā)送的Jaeger thrift數(shù)據(jù)
          9411HTTPcollector接收Zipkin發(fā)送的json或者thrift數(shù)據(jù)
          16686HTTPquery瀏覽器展示端口

          啟動(dòng)后,我們可以訪問 http://localhost:16686 ,在瀏覽器中查看和查詢收集的數(shù)據(jù)。

          由于通過all-in-one鏡像方式收集的數(shù)據(jù)都存儲(chǔ)在docker中,無法持久保存,所以只能用于開發(fā)或者測(cè)試環(huán)境,無法用于生產(chǎn)環(huán)境。生產(chǎn)環(huán)境中需要依據(jù)實(shí)際情況,分別部署各個(gè)組件。

          四、Jaeger在業(yè)務(wù)代碼中的應(yīng)用

          系統(tǒng)中使用Jaeger非常簡(jiǎn)單,只需要在原有程序中插入少量代碼。以下代碼模擬了一個(gè)查詢用戶賬戶余額,執(zhí)行扣款的業(yè)務(wù)場(chǎng)景:

          4.1 初始化jaeger函數(shù)

          主要是按照實(shí)際需要配置有關(guān)參數(shù),例如服務(wù)名稱、采樣模式、采樣比例等等。

          func?initJaeger()?(tracer?opentracing.Tracer,?closer?io.Closer,?err?error)?{
          ?//?構(gòu)造配置信息
          ?cfg?:=?&config.Configuration{
          ??//?設(shè)置服務(wù)名稱
          ??ServiceName:?"ServiceAmount",
          ??//?設(shè)置采樣參數(shù)
          ??Sampler:?&config.SamplerConfig{
          ???Type:??"const",?//?全采樣模式
          ???Param:?1,???????//?開啟狀態(tài)
          ??},
          ?}
          ?
          ?//?生成一條新tracer
          ?tracer,?closer,?err?=?cfg.NewTracer()
          ?if?err?==?nil?{
          ??//?設(shè)置tracer為全局單例對(duì)象
          ??opentracing.SetGlobalTracer(tracer)
          ?}
          ?return
          }

          4.2 檢測(cè)用戶余額函數(shù)

          用于檢測(cè)用戶余額,模擬一個(gè)子任務(wù)Span。

          func?CheckBalance(request?string,?ctx?context.Context)?{
          ?//?創(chuàng)建子span
          ?span,?_?:=?opentracing.StartSpanFromContext(ctx,?"CheckBalance")

          ?//?模擬系統(tǒng)進(jìn)行一系列的操作,耗時(shí)1/3秒
          ?time.Sleep(time.Second?/?3)

          ?//?示例:將需要追蹤的信息放入tag
          ?span.SetTag("request",?request)
          ?span.SetTag("reply",?"CheckBalance?reply")

          ?//?結(jié)束當(dāng)前span
          ?span.Finish()

          ?log.Println("CheckBalance?is?done")
          }

          4.3 從用戶賬戶扣款函數(shù)

          從用戶賬戶扣款,模擬一個(gè)子任務(wù)span。

          func?Reduction(request?string,?ctx?context.Context)?{
          ?//?創(chuàng)建子span
          ?span,?_?:=?opentracing.StartSpanFromContext(ctx,?"Reduction")

          ?//?模擬系統(tǒng)進(jìn)行一系列的操作,耗時(shí)1/2秒
          ?time.Sleep(time.Second?/?2)

          ?//?示例:將需要追蹤的信息放入tag
          ?span.SetTag("request",?request)
          ?span.SetTag("reply",?"Reduction?reply")

          ?//?結(jié)束當(dāng)前span
          ?span.Finish()

          ?log.Println("Reduction?is?done")
          }

          4.4 主函數(shù)

          初始化jaeger環(huán)境,生成tracer,創(chuàng)建父span,以及調(diào)用查詢余額和扣款兩個(gè)子任務(wù)span。

          package?main

          import?(
          ?"context"
          ?"fmt"
          ?"github.com/opentracing/opentracing-go"
          ?"github.com/uber/jaeger-client-go/config"
          ?"io"
          ?"log"
          ?"time"
          )

          func?main()?{
          ?//?初始化jaeger,創(chuàng)建一條新tracer
          ?tracer,?closer,?err?:=?initJaeger()
          ?if?err?!=?nil?{
          ??panic(fmt.Sprintf("ERROR:?cannot?init?Jaeger:?%v\n",?err))
          ?}
          ?defer?closer.Close()

          ?//?創(chuàng)建一個(gè)新span,作為父span,開始計(jì)費(fèi)過程
          ?span :=?tracer.StartSpan("CalculateFee")
          ?
          ?//?生成父span的context
          ?ctx?:=?opentracing.ContextWithSpan(context.Background(),?span)

          ?//?示例:設(shè)置一個(gè)span標(biāo)簽信息
          ?span.SetTag("db.instance",?"customers")
          ?//?示例:輸出一條span日志信息
          ?span.LogKV("event",?"timed?out")

          ?//?將父span的context作為參數(shù),調(diào)用檢測(cè)用戶余額函數(shù)
          ?CheckBalance("CheckBalance?request",?ctx)

          ?//?將父span的context作為參數(shù),調(diào)用扣款函數(shù)
          ?Reduction("Reduction?request",?ctx)

          ?//?結(jié)束父span
          ?span.Finish()
          }

          五、Jaeger在gRPC微服務(wù)中的應(yīng)用

          我們依然模擬了一個(gè)查詢用戶賬戶余額,執(zhí)行扣款的業(yè)務(wù)場(chǎng)景,并把查詢用戶賬戶余額和執(zhí)行扣款功能改造為gRPC微服務(wù):

          5.1 gRPC Server端代碼

          main.go:

          代碼使用了第三方依賴庫(kù)github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing,該依賴庫(kù)將OpenTracing封裝為通用的gRPC中間件,并通過gRPC攔截器無縫嵌入gRPC服務(wù)中。

          package?main

          import?(
          ?"fmt"
          ?"github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing"
          ?"github.com/opentracing/opentracing-go"
          ?"github.com/uber/jaeger-client-go/config"
          ?"google.golang.org/grpc"
          ?"google.golang.org/grpc/reflection"
          ?"grpc-jaeger-server/account"
          ?"io"
          ?"log"
          ?"net"
          )

          //?初始化jaeger
          func?initJaeger()?(tracer?opentracing.Tracer,?closer?io.Closer,?err?error)?{
          ?//?構(gòu)造配置信息
          ?cfg?:=?&config.Configuration{
          ??//?設(shè)置服務(wù)名稱
          ??ServiceName:?"ServiceAmount",

          ??//?設(shè)置采樣參數(shù)
          ??Sampler:?&config.SamplerConfig{
          ???Type:??"const",?//?全采樣模式
          ???Param:?1,???????//?開啟全采樣模式
          ??},
          ?}

          ?//?生成一條新tracer
          ?tracer,?closer,?err?=?cfg.NewTracer()
          ?if?err?==?nil?{
          ??//?設(shè)置tracer為全局單例對(duì)象
          ??opentracing.SetGlobalTracer(tracer)
          ?}
          ?return
          }

          func?main()?{
          ?//?初始化jaeger,創(chuàng)建一條新tracer
          ?tracer,?closer,?err?:=?initJaeger()
          ?if?err?!=?nil?{
          ??panic(fmt.Sprintf("ERROR:?cannot?init?Jaeger:?%v\n",?err))
          ?}
          ?defer?closer.Close()
          ?log.Println("succeed?to?init?jaeger")

          ?//?注冊(cè)gRPC?account服務(wù)
          ?server?:=?grpc.NewServer(grpc.UnaryInterceptor(grpc_opentracing.UnaryServerInterceptor(grpc_opentracing.WithTracer(tracer))))
          ?account.RegisterAccountServer(server,?&AccountServer{})
          ?reflection.Register(server)
          ?log.Println("succeed?to?register?account?service")

          ?//?監(jiān)聽gRPC?account服務(wù)端口
          ?listener,?err?:=?net.Listen("tcp",?":8080")
          ?if?err?!=?nil?{
          ??log.Println(err)
          ??return
          ?}
          ?log.Println("starting?register?account?service")

          ?//?開啟gRpc?account服務(wù)
          ?if?err?:=?server.Serve(listener);?err?!=?nil?{
          ??log.Println(err)
          ??return
          ?}
          }

          計(jì)費(fèi)微服務(wù) accountsever.go:

          package?main

          import?(
          ?"github.com/opentracing/opentracing-go"
          ?"golang.org/x/net/context"
          ?"grpc-jaeger-server/account"
          ?"time"
          )

          //?計(jì)費(fèi)服務(wù)
          type?AccountServer?struct{}

          //?檢測(cè)用戶余額微服務(wù),模擬子span任務(wù)
          func?(s?*AccountServer)?CheckBalance(ctx?context.Context,?request?*account.CheckBalanceRequest)?(response?*account.CheckBalanceResponse,?err?error)?{
          ?response?=?&account.CheckBalanceResponse{
          ??Reply:?"CheckBalance?Reply",?//?處理結(jié)果
          ?}

          ?//?創(chuàng)建子span
          ?span,?_?:=?opentracing.StartSpanFromContext(ctx,?"CheckBalance")

          ?//?模擬系統(tǒng)進(jìn)行一系列的操作,耗時(shí)1/3秒
          ?time.Sleep(time.Second?/?3)

          ?//?將需要追蹤的信息放入tag
          ?span.SetTag("request",?request)
          ?span.SetTag("reply",?response)

          ?//?結(jié)束當(dāng)前span
          ?span.Finish()

          ?return?response,?err
          }

          //?從用戶賬戶扣款微服務(wù),模擬子span任務(wù)
          func?(s?*AccountServer)?Reduction(ctx?context.Context,?request?*account.ReductionRequest)?(response?*account.ReductionResponse,?err?error)?{
          ?response?=?&account.ReductionResponse{
          ??Reply:?"Reduction?Reply",?//?處理結(jié)果
          ?}

          ?//?創(chuàng)建子span
          ?span,?_?:=?opentracing.StartSpanFromContext(ctx,?"Reduction")

          ?//?模擬系統(tǒng)進(jìn)行一系列的操作,耗時(shí)1/3秒
          ?time.Sleep(time.Second?/?3)

          ?//?將需要追蹤的信息放入tag
          ?span.SetTag("request",?request)
          ?span.SetTag("reply",?response)

          ?//?結(jié)束當(dāng)前span
          ?span.Finish()
          ?return?response,?err
          }

          5.2 gRPC Client端代碼main.go:

          package?main

          import?(
          ?"context"
          ?"fmt"
          ?"github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing"
          ?"github.com/opentracing/opentracing-go"
          ?"github.com/uber/jaeger-client-go/config"
          ?"google.golang.org/grpc"
          ?"grpc-jaeger-client/account"
          ?"io"
          ?"log"
          )

          //?初始化jaeger
          func?initJaeger()?(tracer?opentracing.Tracer,?closer?io.Closer,?err?error)?{
          ?//?構(gòu)造配置信息
          ?cfg?:=?&config.Configuration{
          ??//?設(shè)置服務(wù)名稱
          ??ServiceName:?"ServiceAmount",

          ??//?設(shè)置采樣參數(shù)
          ??Sampler:?&config.SamplerConfig{
          ???Type:??"const",?//?全采樣模式
          ???Param:?1,???????//?開啟全采樣模式
          ??},
          ?}

          ?//?生成一條新tracer
          ?tracer,?closer,?err?=?cfg.NewTracer()
          ?if?err?==?nil?{
          ??//?設(shè)置tracer為全局單例對(duì)象
          ??opentracing.SetGlobalTracer(tracer)
          ?}
          ?return
          }

          func?main()?{
          ?//?初始化jaeger,創(chuàng)建一條新tracer
          ?tracer,?closer,?err?:=?initJaeger()
          ?if?err?!=?nil?{
          ??panic(fmt.Sprintf("ERROR:?cannot?init?Jaeger:?%v\n",?err))
          ?}
          ?defer?closer.Close()
          ?log.Println("succeed?to?init?jaeger")

          ?//?創(chuàng)建一個(gè)新span,作為父span
          ?span :=?tracer.StartSpan("CalculateFee")

          ?//?函數(shù)返回時(shí)關(guān)閉span
          ?defer?span.Finish()

          ?//?生成span的context
          ?ctx?:=?opentracing.ContextWithSpan(context.Background(),?span)

          ?//?連接gRPC?server
          ?conn,?err?:=?grpc.Dial("localhost:8080",
          ??grpc.WithInsecure(),
          ??grpc.WithUnaryInterceptor(grpc_opentracing.UnaryClientInterceptor(grpc_opentracing.WithTracer(tracer),
          ??)))
          ?if?err?!=?nil?{
          ??log.Println(err)
          ??return
          ?}

          ?//?創(chuàng)建gRPC計(jì)費(fèi)服務(wù)客戶端
          ?client?:=?account.NewAccountClient(conn)

          ?//?將父span的context作為參數(shù),調(diào)用檢測(cè)用戶余額的gRPC微服務(wù)
          ?checkBalanceResponse,?err?:=?client.CheckBalance(ctx,
          ??&account.CheckBalanceRequest{
          ???Account:?"user?account",
          ??})
          ?if?err?!=?nil?{
          ??log.Println(err)
          ??return
          ?}
          ?log.Println(checkBalanceResponse)

          ?//?將父span的context作為參數(shù),調(diào)用扣款的gRPC微服務(wù)
          ?reductionResponse,?err?:=?client.Reduction(ctx,
          ??&account.ReductionRequest{
          ???Account:?"user?account",
          ???Amount:?1,
          ??})
          ?if?err?!=?nil?{
          ??log.Println(err)
          ??return
          ?}
          ?log.Println(reductionResponse)
          }

          注:
          本文全部源代碼位于:https://github.com/wangshizebin/micro-service





          粉絲福利:實(shí)戰(zhàn)springboot+CAS單點(diǎn)登錄系統(tǒng)視頻教程免費(fèi)領(lǐng)取

          ???

          ?長(zhǎng)按上方微信二維碼?2 秒
          即可獲取資料



          感謝點(diǎn)贊支持下哈?

          瀏覽 40
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩中文字幕在线免费视频 | 一起草成人视频 | 美女操逼国产 | 99久久久久 | 唐嫣一区二区三区在线 |