<ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>


      在微服務(wù)架構(gòu)中,調(diào)用鏈?zhǔn)锹L(zhǎng)而復(fù)雜的,要了解其中的每個(gè)環(huán)節(jié)及其性能,你需要全鏈路跟蹤。
      它的原理很簡(jiǎn)單,你可以在每個(gè)請(qǐng)求開始時(shí)生成一個(gè)唯一的ID,并將其傳遞到整個(gè)調(diào)用鏈。 該ID稱為CorrelationID
      <https://hilton.org.uk/blog/microservices-correlation-id>
      1,你可以用它來跟蹤整個(gè)請(qǐng)求并獲得各個(gè)調(diào)用環(huán)節(jié)的性能指標(biāo)。簡(jiǎn)單來說有兩個(gè)問題需要解決。第一,如何在應(yīng)用程序內(nèi)部傳遞ID;
      第二,當(dāng)你需要調(diào)用另一個(gè)微服務(wù)時(shí),如何通過網(wǎng)絡(luò)傳遞ID。

      什么是OpenTracing?

      現(xiàn)在有許多開源的分布式跟蹤庫(kù)可供選擇,其中最受歡迎的庫(kù)可能是Zipkin <https://zipkin.io/>2和Jaeger
      <https://www.jaegertracing.io/>3。
      選擇哪個(gè)是一個(gè)令人頭疼的問題,因?yàn)槟悻F(xiàn)在可以選擇最受歡迎的一個(gè),但是如果以后有一個(gè)更好的出現(xiàn)呢?OpenTracing
      <https://opentracing.io/docs/getting-started/>
      ?可以幫你解決這個(gè)問題。它建立了一套跟蹤庫(kù)的通用接口,這樣你的程序只需要調(diào)用這些接口而不被具體的跟蹤庫(kù)綁定,將來可以切換到不同的跟蹤庫(kù)而無(wú)需更改代碼。Zipkin和Jaeger都支持OpenTracing。

      如何跟蹤服務(wù)器端點(diǎn)(server endpoints)?

      在下面的程序中我使用“Zipkin”作為跟蹤庫(kù),用“OpenTracing”作為通用跟蹤接口。 跟蹤系統(tǒng)中通常有四個(gè)組件,下面我用Zipkin作為示例:

      *
      recorder(記錄器):記錄跟蹤數(shù)據(jù)

      *
      Reporter (or collecting agent)(報(bào)告器或收集代理):從記錄器收集數(shù)據(jù)并將數(shù)據(jù)發(fā)送到UI程序

      *
      Tracer:生成跟蹤數(shù)據(jù)

      *
      UI:負(fù)責(zé)在圖形UI中顯示跟蹤數(shù)據(jù)



      上面是Zipkin的組件圖,你可以在Zipkin Architecture
      <https://zipkin.io/pages/architecture.html>中找到它。

      有兩種不同類型的跟蹤,一種是進(jìn)程內(nèi)跟蹤(in-process),另一種是跨進(jìn)程跟蹤(cross-process)。 我們將首先討論跨進(jìn)程跟蹤。

      客戶端程序:

      我們將用一個(gè)簡(jiǎn)單的gRPC程序作為示例,它分成客戶端和服務(wù)器端代碼。 我們想跟蹤一個(gè)完整的服務(wù)請(qǐng)求,它從客戶端到服務(wù)端并從服務(wù)端返回。
      以下是在客戶端創(chuàng)建新跟蹤器的代碼。它首先創(chuàng)建“HTTP Collector”(the agent)用來收集跟蹤數(shù)據(jù)并將其發(fā)送到“Zipkin” UI,
      “endpointUrl”是“Zipkin” UI的URL。
      其次,它創(chuàng)建了一個(gè)記錄器(recorder)來記錄端點(diǎn)上的信息,“hostUrl”是gRPC(客戶端)呼叫的URL。第三,它用我們新建的記錄器創(chuàng)建了一個(gè)新的跟蹤器(tracer)。
      最后,它為“OpenTracing”設(shè)置了“GlobalTracer”,這樣你可以在程序中的任何地方訪問它。
      const ( endpoint_url = "http://localhost:9411/api/v1/spans" host_url =
      "localhost:5051" service_name_cache_client = "cache service client"
      service_name_call_get = "callGet" ) func newTracer () (opentracing.Tracer,
      zipkintracer.Collector, error) { collector, err :=
      openzipkin.NewHTTPCollector(endpoint_url) if err != nil { return nil, nil, err
      } recorder :=openzipkin.NewRecorder(collector, true, host_url,
      service_name_cache_client) tracer, err := openzipkin.NewTracer( recorder,
      openzipkin.ClientServerSameSpan(true)) if err != nil { return nil,nil,err }
      opentracing.SetGlobalTracer(tracer) return tracer,collector, nil }
      以下是gRPC客戶端代碼。
      它首先調(diào)用上面提到的函數(shù)“newTrace()”來創(chuàng)建跟蹤器,然后,它創(chuàng)建一個(gè)包含跟蹤器的gRPC調(diào)用連接。接下來,它使用新建的gRPC連接創(chuàng)建緩存服務(wù)(Cache
      service)的gRPC客戶端。 最后,它通過gRPC客戶端來調(diào)用緩存服務(wù)的“Get”函數(shù)。
      key:="123" tracer, collector, err :=newTracer() if err != nil { panic(err) }
      defer collector.Close() connection, err := grpc.Dial(host_url,
      grpc.WithInsecure(),
      grpc.WithUnaryInterceptor(otgrpc.OpenTracingClientInterceptor(tracer,
      otgrpc.LogPayloads())), ) if err != nil { panic(err) } defer connection.Close()
      client := pb.NewCacheServiceClient(connection) value, err := callGet(key,
      client)
      Trace 和 Span:

      在OpenTracing中,一個(gè)重要的概念是“trace”,它表示從頭到尾的一個(gè)請(qǐng)求的調(diào)用鏈,它的標(biāo)識(shí)符是“traceID”。
      一個(gè)“trace”包含有許多跨度(span),每個(gè)跨度捕獲調(diào)用鏈內(nèi)的一個(gè)工作單元,并由“spanId”標(biāo)識(shí)。
      每個(gè)跨度具有一個(gè)父跨度,并且一個(gè)“trace”的所有跨度形成有向無(wú)環(huán)圖(DAG)。 以下是跨度之間的關(guān)系圖。 你可以從The OpenTracing
      Semantic Specification <https://opentracing.io/specification/>中找到它。



      以下是函數(shù)“callGet”的代碼,它調(diào)用了gRPC服務(wù)端的“Get"函數(shù)。
      在函數(shù)的開頭,OpenTracing為這個(gè)函數(shù)調(diào)用開啟了一個(gè)新的span,整個(gè)函數(shù)結(jié)束后,它也結(jié)束了這個(gè)span。
      const service_name_call_get = "callGet" func callGet(key string, c
      pb.CacheServiceClient) ( []byte, error) { span :=
      opentracing.StartSpan(service_name_call_get) defer span.Finish()
      time.Sleep(5*time.Millisecond) // Put root span in context so it will be used
      in our calls to the client. ctx :=
      opentracing.ContextWithSpan(context.Background(), span) //ctx :=
      context.Background() getReq:=&pb.GetReq{Key:key} getResp, err :=c.Get(ctx,
      getReq ) value := getResp.Value return value, err }
      服務(wù)端代碼:


      下面是服務(wù)端代碼,它與客戶端代碼類似,它調(diào)用了“newTracer()”(與客戶端“newTracer()”函數(shù)幾乎相同)來創(chuàng)建跟蹤器。然后,它創(chuàng)建了一個(gè)“OpenTracingServerInterceptor”,其中包含跟蹤器。
      最后,它使用我們剛創(chuàng)建的攔截器(Interceptor)創(chuàng)建了gRPC服務(wù)器。
      connection, err := net.Listen(network, host_url) if err != nil { panic(err) }
      tracer,err := newTracer() if err != nil { panic(err) } opts :=
      []grpc.ServerOption{ grpc.UnaryInterceptor(
      otgrpc.OpenTracingServerInterceptor(tracer,otgrpc.LogPayloads()), ), } srv :=
      grpc.NewServer(opts...) cs := initCache() pb.RegisterCacheServiceServer(srv,
      cs) err = srv.Serve(connection) if err != nil { panic(err) } else {
      fmt.Println("server listening on port 5051") }
      以下是運(yùn)行上述代碼后在Zipkin中看到的跟蹤和跨度的圖片。
      在服務(wù)器端,我們不需要在函數(shù)內(nèi)部編寫任何代碼來生成span,我們需要做的就是創(chuàng)建跟蹤器(tracer),服務(wù)器攔截器自動(dòng)為我們生成span。



      怎樣跟蹤函數(shù)內(nèi)部?

      上面的圖片沒有告訴我們函數(shù)內(nèi)部的跟蹤細(xì)節(jié), 我們需要編寫一些代碼來獲得它。

      以下是服務(wù)器端“get”函數(shù),我們?cè)谄渲刑砑恿烁櫞a。 它首先從上下文獲取跨度(span),然后創(chuàng)建一個(gè)新的子跨度并使用我們剛剛獲得的跨度作為父跨度。
      接下來,它執(zhí)行一些操作(例如數(shù)據(jù)庫(kù)查詢),然后結(jié)束(mysqlSpan.Finish())子跨度。
      const service_name_db_query_user = "db query user" func (c *CacheService)
      Get(ctx context.Context, req *pb.GetReq) (*pb.GetResp, error) {
      time.Sleep(5*time.Millisecond) if parent := opentracing.SpanFromContext(ctx);
      parent != nil { pctx := parent.Context() if tracer :=
      opentracing.GlobalTracer(); tracer != nil { mysqlSpan :=
      tracer.StartSpan(service_name_db_query_user, opentracing.ChildOf(pctx)) defer
      mysqlSpan.Finish() //do some operations time.Sleep(time.Millisecond * 10) } }
      key := req.GetKey() value := c.storage[key] fmt.Println("get called with return
      of value: ", value) resp := &pb.GetResp{Value: value} return resp, nil }
      以下是它運(yùn)行后的圖片。 現(xiàn)在它在服務(wù)器端有一個(gè)新的跨度“db query user”。



      以下是zipkin中的跟蹤數(shù)據(jù)。 你可以看到客戶端從8.016ms開始,服務(wù)端也在同一時(shí)間啟動(dòng)。 服務(wù)器端完成需要大約16ms。



      怎樣跟蹤數(shù)據(jù)庫(kù)?


      怎樣才能跟蹤數(shù)據(jù)庫(kù)內(nèi)部的操作?首先,數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序需要支持跟蹤,另外你需要將跟蹤器(tracer)傳遞到數(shù)據(jù)庫(kù)函數(shù)中。如果數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序不支持跟蹤怎么辦?現(xiàn)在已經(jīng)有幾個(gè)開源驅(qū)動(dòng)程序封裝器(Wrapper),它們可以封裝任何數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序并使其支持跟蹤。其中一個(gè)是
      instrumentedsql <https://github.com/ExpansiveWorlds/instrumentedsql>?(另外兩個(gè)是
      luna-duclos/instrumentedsql <https://github.com/luna-duclos/instrumentedsql>?和
      ocsql/driver.go
      <https://github.com/opencensus-integrations/ocsql/blob/master/driver.go>
      ?)。我簡(jiǎn)要地看了一下他們的代碼,他們的原理基本相同。它們都為底層數(shù)據(jù)庫(kù)的每個(gè)函數(shù)創(chuàng)建了一個(gè)封裝(Wrapper),并在每個(gè)數(shù)據(jù)庫(kù)操作之前啟動(dòng)一個(gè)新的跨度,并在操作完成后結(jié)束跨度。但是所有這些都只封裝了“database/sql”接口,這就意味著NoSQL數(shù)據(jù)庫(kù)沒有辦法使用他們。如果你找不到支持你需要的NoSQL數(shù)據(jù)庫(kù)(例如MongoDB)的OpenTracing的驅(qū)動(dòng)程序,你可能需要自己編寫一個(gè)封裝(Wrapper),它并不困難。


      一個(gè)問題是“如果我使用OpenTracing和Zipkin而數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序使用Openeracing和Jaeger,那會(huì)有問題嗎?"這其實(shí)不會(huì)發(fā)生。我上面提到的大部分封裝都支持OpenTracing。在使用封裝時(shí),你需要注冊(cè)封裝了的SQL驅(qū)動(dòng)程序,其中包含跟蹤器。在SQL驅(qū)動(dòng)程序內(nèi)部,所有跟蹤函數(shù)都只調(diào)用了OpenTracing的接口,因此它們甚至不知道底層實(shí)現(xiàn)是Zipkin還是Jaeger?,F(xiàn)在使用OpenTarcing的好處終于體現(xiàn)出來了。在應(yīng)用程序中創(chuàng)建全局跟蹤器時(shí)(Global
      tracer),你需要決定是使用Zipkin還是Jaeger,但這之后,應(yīng)用程序或第三方庫(kù)中的每個(gè)函數(shù)都只調(diào)用OpenTracing接口,已經(jīng)與具體的跟蹤庫(kù)(Zipkin或Jaeger)沒關(guān)系了。

      怎樣跟蹤服務(wù)調(diào)用?

      假設(shè)我們需要在gRPC服務(wù)中調(diào)用另外一個(gè)微服務(wù)(例如RESTFul服務(wù)),該如何跟蹤?


      簡(jiǎn)單來說就是使用HTTP頭作為媒介(Carrier)來傳遞跟蹤信息(traceID)。無(wú)論微服務(wù)是gRPC還是RESTFul,它們都使用HTTP協(xié)議。如果是消息隊(duì)列(Message
      Queue),則將跟蹤信息(traceID)放入消息報(bào)頭中。(Zipkin B3-propogation
      <https://github.com/openzipkin/b3-propagation>有“single header”和“multiple
      header”有兩種不同類型的跟蹤信息,但JMS僅支持“single header”)

      一個(gè)重要的概念是“跟蹤上下文(trace
      context)”,它定義了傳播跟蹤所需的所有信息,例如traceID,parentId(父spanId)等。有關(guān)詳細(xì)信息,請(qǐng)閱讀跟蹤上下文(trace
      context) <https://www.w3.org/TR/trace-context/>1?。

      OpenTracing提供了兩個(gè)處理“跟蹤上下文(trace
      context)”的函數(shù):“extract(format,carrier)”和“inject(SpanContext,format,carrier)”。
      “extarct()”從媒介(通常是HTTP頭)獲取跟蹤上下文。 “inject”將跟蹤上下文放入媒介,來保證跟蹤鏈的連續(xù)性。以下是我從Zipkin獲取的
      b3-propagation <https://github.com/openzipkin/b3-propagation>圖。




      但是為什么我們沒有在上面的例子中調(diào)用這些函數(shù)呢?讓我們?cè)賮砘仡櫼幌麓a。在客戶端,在創(chuàng)建gRPC客戶端連接時(shí),我們調(diào)用了一個(gè)為“OpenTracingClientInterceptor”的函數(shù)。
      以下是“OpenTracingClientInterceptor”的部分代碼,我從otgrpc
      <http://github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc>
      11包中的“client.go”中得到了它。它已經(jīng)從Go context <https://blog.golang.org/context>
      12獲取了跟蹤上下文并將其注入HTTP頭,因此我們不再需要再次調(diào)用“inject”函數(shù)。
      func OpenTracingClientInterceptor(tracer opentracing.Tracer, optFuncs
      ...Option) grpc.UnaryClientInterceptor { ... ctx = injectSpanContext(ctx,
      tracer, clientSpan) ... } func injectSpanContext(ctx context.Context, tracer
      opentracing.Tracer, clientSpan opentracing.Span) context.Context { md, ok :=
      metadata.FromOutgoingContext(ctx) if !ok { md = metadata.New(nil) } else { md =
      md.Copy() } mdWriter := metadataReaderWriter{md} err :=
      tracer.Inject(clientSpan.Context(), opentracing.HTTPHeaders, mdWriter) // We
      have no better place to record an error than the Span itself :-/ if err != nil
      { clientSpan.LogFields(log.String("event", "Tracer.Inject() failed"),
      log.Error(err)) } return metadata.NewOutgoingContext(ctx, md) }

      在服務(wù)器端,我們還調(diào)用了一個(gè)函數(shù)“otgrpc.OpenTracingServerInterceptor”,其代碼類似于客戶端的“OpenTracingClientInterceptor”。它不是調(diào)用“inject”寫入跟蹤上下文,而是從HTTP頭中提?。╡xtract)跟蹤上下文并將其放入Go上下文(Go
      context)中。 這就是我們不需要再次手動(dòng)調(diào)用“extract()”的原因。
      我們可以直接從Go上下文中提取跟蹤上下文(opentracing.SpanFromContext(ctx))。
      但對(duì)于其他基于HTTP的服務(wù)(如RESTFul服務(wù)), 情況就并非如此,因此我們需要寫代碼從服務(wù)器端的HTTP頭中提取跟蹤上下文。
      當(dāng)然,您也可以使用攔截器或過濾器。

      跟蹤庫(kù)之間的互兼容性


      你也許會(huì)問“如果我的程序使用Zipkin和OpenTracing而需要調(diào)用的第三方微服務(wù)使用OpenTracing與Jaeger,它們會(huì)兼容嗎?"它看起來于我們之前詢問的數(shù)據(jù)庫(kù)問題類似,但實(shí)際上很不相同。對(duì)于數(shù)據(jù)庫(kù),因?yàn)閼?yīng)用程序和數(shù)據(jù)庫(kù)在同一個(gè)進(jìn)程中,它們可以共享相同的全局跟蹤器,因此更容易解決。對(duì)于微服務(wù),這種方式將不兼容。因?yàn)镺penTracing只標(biāo)準(zhǔn)化了跟蹤接口,它沒有標(biāo)準(zhǔn)化跟蹤上下文。萬(wàn)維網(wǎng)聯(lián)盟(W3C)正在制定
      跟蹤上下文(trace context) <https://w3c.github.io/trace-context/>
      1?的標(biāo)準(zhǔn),并于2019-08-09年發(fā)布了候選推薦標(biāo)準(zhǔn)。OpenTracing沒有規(guī)定跟蹤上下文的格式,而是把決定權(quán)留給了實(shí)現(xiàn)它的跟蹤庫(kù)。結(jié)果每個(gè)庫(kù)都選擇了自己獨(dú)有的的格式。例如,Zipkin使用“X-B3-TraceId”作為跟蹤ID,Jaeger使用“uber-trace-id”,因此使用OpenTracing并不意味著不同的跟蹤庫(kù)可以進(jìn)行跨網(wǎng)互操作。
      對(duì)于“Jaeger”來說有一個(gè)好處是你可以選擇使用“Zipkin兼容性功能
      <https://github.com/jaegertracing/jaeger-client-go/tree/master/zipkin>
      "13來生成Zipkin跟蹤上下文,
      這樣就可以與Zipkin相互兼容了。對(duì)于其他情況,你需要自己進(jìn)行手動(dòng)格式轉(zhuǎn)換(在“inject”和“extract”之間)。

      全鏈路跟蹤設(shè)計(jì)

      盡量少寫代碼

      一個(gè)好的全鏈路跟蹤系統(tǒng)不需要用戶編寫很多跟蹤代碼。最理想的情況是你不需要任何代碼,讓框架或庫(kù)負(fù)責(zé)處理它,當(dāng)然這比較困難。 全鏈路跟蹤分成三個(gè)跟蹤級(jí)別:

      *
      跨進(jìn)程跟蹤 (cross-process)(調(diào)用另一個(gè)微服務(wù))

      *
      數(shù)據(jù)庫(kù)跟蹤

      *
      進(jìn)程內(nèi)部的跟蹤 (in-process)(在一個(gè)函數(shù)內(nèi)部的跟蹤)


      跨進(jìn)程跟蹤是最簡(jiǎn)單的。你可以編寫攔截器或過濾器來跟蹤每個(gè)請(qǐng)求,它只需要編寫極少的編碼。數(shù)據(jù)庫(kù)跟蹤也比較簡(jiǎn)單。如果使用我們上面討論過的封裝器(Wrapper),你只需要注冊(cè)SQL驅(qū)動(dòng)程序封裝器(Wrapper)并將go-context(里面有跟蹤上下文)
      傳入數(shù)據(jù)庫(kù)函數(shù)。你可以使用依賴注入(Dependency Injection)這樣就可以用比較少的代碼來完成此操作。


      進(jìn)程內(nèi)跟蹤是最困難的,因?yàn)槟惚仨殲槊總€(gè)單獨(dú)的函數(shù)編寫跟蹤代碼?,F(xiàn)在還沒有一個(gè)很好的方法,可以編寫一個(gè)通用的函數(shù)來跟蹤應(yīng)用程序中的每個(gè)函數(shù)(攔截器不是一個(gè)好選擇,因?yàn)樗枰總€(gè)函數(shù)的參數(shù)和返回都必須是一個(gè)泛型類型(interface
      {}))。幸運(yùn)的是,對(duì)于大多數(shù)人來說,前兩個(gè)級(jí)別的跟蹤應(yīng)該已經(jīng)足夠了。

      有些人可能會(huì)使用服務(wù)網(wǎng)格(service mesh)來實(shí)現(xiàn)分布式跟蹤,例如Istio <https://istio.io/>或Linkerd
      <https://linkerd.io/>
      。它確實(shí)是一個(gè)好主意,跟蹤最好由基礎(chǔ)架構(gòu)實(shí)現(xiàn),而不是將業(yè)務(wù)邏輯代碼與跟蹤代碼混在一起,不過你將遇到我們剛才談到的同樣問題。服務(wù)網(wǎng)格只負(fù)責(zé)跨進(jìn)程跟蹤,函數(shù)內(nèi)部或數(shù)據(jù)庫(kù)跟蹤任然需要你來編寫代碼。不過一些服務(wù)網(wǎng)格可以通過提供與流行跟蹤庫(kù)的集成,來簡(jiǎn)化不同跟蹤庫(kù)跨網(wǎng)跟蹤時(shí)的的上下文格式轉(zhuǎn)換。

      跟蹤設(shè)計(jì):

      精心設(shè)計(jì)的跨度(span),服務(wù)名稱(service name),標(biāo)簽(tag)能充分發(fā)揮全鏈路跟蹤的作用,并使之簡(jiǎn)單易用。有關(guān)信息請(qǐng)閱讀
      語(yǔ)義約定(Semantic Conventions)
      <https://github.com/opentracing/specification/blob/master/semantic_conventions.md>
      1?。

      將Trace ID記錄到日志

      將跟蹤與日志記錄集成是一個(gè)常見的需求,最重要的是將跟蹤ID記錄到整個(gè)調(diào)用鏈的日志消息中。 目前OpenTracing不提供訪問traceID的方法。
      你可以將“OpenTracing.SpanContext”轉(zhuǎn)換為特定跟蹤庫(kù)的“SpanContext”(Zipkin和Jaeger都可以通過“SpanContext”訪問traceID)或?qū)ⅰ癘penTracing.SpanContext”轉(zhuǎn)換為字符串并解析它以獲取traceID。轉(zhuǎn)換為字符串更好,因?yàn)樗粫?huì)破壞程序的依賴關(guān)系。
      幸運(yùn)的是不久的將來你就不需要它了,因?yàn)镺penTracing將提供訪問traceID的方法,請(qǐng)閱讀這里
      <https://github.com/opentracing/specification/blob/master/rfc/trace_identifiers.md>


      OpenTracing 和 OpenCensus

      OpenCensus <https://opencensus.io/>
      1?不是另一個(gè)通用跟蹤接口,它是一組庫(kù),可以用來與其他跟蹤庫(kù)集成以完成跟蹤功能,因此它經(jīng)常與OpenTracing進(jìn)行比較。
      那么它與OpenTracing兼容嗎?答案是否定的。
      因此,在選擇跟蹤接口時(shí)(不論是OpenTracing還是OpenCensus)需要小心,以確保你需要調(diào)用的其他庫(kù)支持它。
      一個(gè)好消息是,你不需要在將來做出選擇,因?yàn)樗鼈儠?huì)將項(xiàng)目合并為一個(gè)
      <https://medium.com/opentracing/merging-opentracing-and-opencensus-f0fe9c7ca6f0>
      1?。

      結(jié)論:

      全鏈路跟蹤包括不同的場(chǎng)景,例如在函數(shù)內(nèi)部跟蹤,數(shù)據(jù)庫(kù)跟蹤和跨進(jìn)程跟蹤。
      每個(gè)場(chǎng)景都有不同的問題和解決方案。如果你想設(shè)計(jì)更好的跟蹤解決方案或?yàn)槟愕膽?yīng)用選擇最適合的跟蹤工具或庫(kù),那你需要對(duì)每種情況都有清晰的了解。

      源碼:

      完整源碼的github鏈接 <https://github.com/jfeng45/grpcservice>

      索引:

      [1]Correlation IDs for microservices architectures
      https://hilton.org.uk/blog/microservices-correlation-id
      <https://hilton.org.uk/blog/microservices-correlation-id>

      [2]Zipkin
      https://zipkin.io <https://zipkin.io/>

      [3]Jaeger: open source, end-to-end distributed tracing
      https://www.jaegertracing.io <https://www.jaegertracing.io/>

      [4]OpenTracing
      https://opentracing.io/docs/getting-started
      <https://opentracing.io/docs/getting-started/>

      [5]Zipkin Architecture
      https://zipkin.io/pages/architecture.html
      <https://zipkin.io/pages/architecture.html>

      [6]The OpenTracing Semantic Specification
      https://opentracing.io/specification/ <https://opentracing.io/specification/>

      [7]instrumentedsql
      https://github.com/ExpansiveWorlds/instrumentedsql
      <https://github.com/ExpansiveWorlds/instrumentedsql>

      [8]luna-duclos/instrumentedsql
      https://github.com/luna-duclos/instrumentedsql
      <https://github.com/luna-duclos/instrumentedsql>

      [9]ocsql/driver.go
      https://github.com/opencensus-integrations/ocsql/blob/master/driver.go
      <https://github.com/opencensus-integrations/ocsql/blob/master/driver.go>

      [10]Trace Context
      https://www.w3.org/TR/trace-context/ <https://www.w3.org/TR/trace-context/>

      [11]otgrpc
      http://github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc
      <http://github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc>

      [12]Go Concurrency Patterns: Context
      https://blog.golang.org/context <https://blog.golang.org/context>

      [13]Zipkin compatibility features
      https://github.com/jaegertracing/jaeger-client-go/tree/master/zipkin
      <https://github.com/jaegertracing/jaeger-client-go/tree/master/zipkin>

      [14]Semantic Conventions

      https://github.com/opentracing/specification/blob/master/semantic_conventions.md

      <https://github.com/opentracing/specification/blob/master/semantic_conventions.md>

      [15]OpenCensus
      https://opencensus.io/ <https://opencensus.io/>

      [16]merge the project into one
      https://medium.com/opentracing/merging-opentracing-and-opencensus-f0fe9c7ca6f0
      <https://medium.com/opentracing/merging-opentracing-and-opencensus-f0fe9c7ca6f0>

      友情鏈接
      ioDraw流程圖
      API參考文檔
      OK工具箱
      云服務(wù)器優(yōu)惠
      阿里云優(yōu)惠券
      騰訊云優(yōu)惠券
      京東云優(yōu)惠券
      站點(diǎn)信息
      問題反饋
      郵箱:[email protected]
      QQ群:637538335
      關(guān)注微信

        <ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>
          香港美女逼逼网 | 日本一区二区三区免费观看 | 国产丝袜一区二区三区免费观看 | 堆萌操逼网站 | 国产在线观看国产精品产拍 | aaa视频 | 成人国产精品人妻人伦a 6 2v久 欧美午夜福利在线观看 | 久久久久久久国产精品 | 国产精品99久久久久久一二区 | 成人自拍视频网 |