分佈式鏈路追蹤續集

本文是 分佈式鏈路追蹤 的續集。

在上一文中提到爲了統一各種分佈式追蹤系統的實現,CNCF (雲原生計算基金會)下的 OpenTracing 項目定義了一套分佈式追蹤的標準,可以使用 OpenTracing API 完成代碼的監控埋點,最後用戶自由選擇遵循 OpenTracing 標準的鏈路追蹤系統,比如 jaeger 。

但是現在訪問 OpenTracing 的 官網 [1] ,可以發現官網提醒 OpenTracing 和 OpenCensus 已經被合併成爲 OpenTelemetry 。(在上文編寫時我未發現到,感謝 Donald Liu 大佬的提醒)

在 cncf[2] 上也可以發現 OpenTracing 已經被 Archived 了

相應地,OpenTelemetry 已經正式成爲了 CNCF 的孵化項目 參考 [3]

Both OpenTracing and OpenCensus will be further deprecated in the coming weeks, with OpenTracing being formally archived by the CNCF TOC.

緣由

參考 [4]

在 APM (Application Performance Monitoring) 領域,或者說微服務的可觀察性方面包括有分佈式鏈路追蹤,指標監控和日誌。

OpenTracing 是最早爲分佈式追蹤制定了一套平臺無關、廠商無關的協議標準的項目,並以此成爲了 CNCF 的孵化項目。

在之後,谷歌牽頭,微軟加入,創建了 OpenCensus 項目統一 Metrics 基礎指標監控的使用方式,還做了 OpenTracing 的老本行:分佈式追蹤。

一山不容二虎,OpenTracing 和 OpenCensus 愈打愈烈,對我們用戶來講,實在是太不友好了。

然後 OpenTelemetry 橫空出世了,OpenTracing 和 OpenCensus 既然都這麼好,乾脆你們合併起來吧,我 OpenTelemetry 來兼容你們。

OpenTelemetry 的自身定位十分明確:數據採集和標準規範的統一,對於數據如何去使用、存儲、展示、告警,官方是不涉及的。

OpenTelemetry 的終極目標十分偉大:實現 Metrics、Tracing、Logging 的融合及大一統,作爲 APM 的數據採集終極解決方案。

然後就是現在的故事了,OpenTelemetry 正式成爲 CNCF 的孵化項目,OpenTracing 和 OpenCensus 不再維護。

兼容性

這個放心,大部分的知識點定義還是一致的 (如數據模型:Trace, Span, SpanContext) ,但是 API 的使用會有所區別,詳細可以查看 OpenTelemetry 規範文檔 [5]

承接上文

我們現在對上文的 OpenTracing API 的代碼做出修改,擁抱 OpenTelemetry 吧!

安裝 OpenTelemetry for Go 依賴

go get go.opentelemetry.io/otel@v1.0.0-RC1 go.opentelemetry.io/otel/sdk@v1.0.0-RC1 go.opentelemetry.io/otel/exporters/stdout/stdouttrace@v1.0.0-RC1 go.opentelemetry.io/otel/trace@v1.0.0-RC1

安裝 OpenTelemetry for jaeger 依賴

go get -u go.opentelemetry.io/otel/exporters/jaeger

OpenTelemetry 官方爲多種開源框架提供了開箱即用的 Instrumentation Packages ,如 gin , beego , mux , go-kit 等,當然也支持 net/http 標準庫 ,更多可瀏覽 opentelemetry-go-contrib[6]

我們使用的是 net/http 標準庫,所以直接導入 otelhttp

go get go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp

main.go 修改如下:

package main

import (
 "fmt"
 "log"
 "math/rand"
 "net/http"
 "time"

 "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
 "go.opentelemetry.io/otel"
 "go.opentelemetry.io/otel/attribute"
 "go.opentelemetry.io/otel/exporters/jaeger"
 "go.opentelemetry.io/otel/propagation"
 "go.opentelemetry.io/otel/sdk/resource"
 tracesdk "go.opentelemetry.io/otel/sdk/trace"
 semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
 "go.opentelemetry.io/otel/trace"
)

func init() {
 exp, err := jaeger.New(jaeger.WithAgentEndpoint())
 if err != nil {
  panic(err)
 }
 tp := tracesdk.NewTracerProvider(
  tracesdk.WithSampler(tracesdk.AlwaysSample()),
  tracesdk.WithBatcher(exp),
  tracesdk.WithResource(resource.NewWithAttributes(
   semconv.SchemaURL,
   semconv.ServiceNameKey.String("opentelemetry-example"), // 服務名
   semconv.ServiceVersionKey.String("0.0.1"),
  )),
 )
 otel.SetTracerProvider(tp)
 otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
}

func main() {
 port := 8080
 addr := fmt.Sprintf(":%d", port)
 mux := http.NewServeMux()
 mux.HandleFunc("/", indexHandler)
 mux.Handle("/home", otelhttp.NewHandler(http.HandlerFunc(homeHandler)"請求 /home"))
 mux.Handle("/async", otelhttp.NewHandler(http.HandlerFunc(serviceHandler)"請求 /async"))
 mux.Handle("/service", otelhttp.NewHandler(http.HandlerFunc(serviceHandler)"請求 /service"))
 mux.Handle("/db", otelhttp.NewHandler(http.HandlerFunc(dbHandler)"請求 /db"))
 fmt.Printf("http://localhost:%d\n", port)
 log.Fatal(http.ListenAndServe(addr, mux))
}

// 主頁 Html
func indexHandler(w http.ResponseWriter, r *http.Request) {
 w.Write([]byte(`<a href="/home"> 點擊開始發起請求 </a>`))
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
 w.Write([]byte("開始請求...\n"))

 ctx := r.Context()
 span := trace.SpanFromContext(ctx)
 defer span.End()

 // 發起異步請求
 asyncReq, _ := http.NewRequest("GET""http://localhost:8080/async", nil)
 // 傳遞span的上下文信息
 // 將關於本地追蹤調用的span context,設置到http header上,並傳遞出去
 otel.GetTextMapPropagator().Inject(ctx,
  propagation.HeaderCarrier(asyncReq.Header),
 )
 go func() {
  if _, err := http.DefaultClient.Do(asyncReq); err != nil {
   span.RecordError(err)
   span.SetAttributes(
    attribute.String("請求 /async error", err.Error()),
   )
  }
 }()

 time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)

 // 發起同步請求
 syncReq, _ := http.NewRequest("GET""http://localhost:8080/service", nil)
 otel.GetTextMapPropagator().Inject(ctx,
  propagation.HeaderCarrier(syncReq.Header),
 )
 if _, err := http.DefaultClient.Do(syncReq); err != nil {
  span.RecordError(err)
  span.SetAttributes(
   attribute.String("請求 /service error", err.Error()),
  )
 }
 w.Write([]byte("請求結束!"))
}

// 模擬業務請求
func serviceHandler(w http.ResponseWriter, r *http.Request) {
 // 通過http header,提取span元數據信息
 span := trace.SpanFromContext(
  otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header)),
 )
 defer span.End()

 dbReq, _ := http.NewRequest("GET""http://localhost:8080/db", nil)
 otel.GetTextMapPropagator().Inject(r.Context(), propagation.HeaderCarrier(dbReq.Header))
 if _, err := http.DefaultClient.Do(dbReq); err != nil {
  span.RecordError(err)
  span.SetAttributes(
   attribute.String("請求 /db error", err.Error()),
  )
 }

 time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)
}

// 模擬DB調用
func dbHandler(w http.ResponseWriter, r *http.Request) {
 // 通過http header,提取span元數據信息
 span := trace.SpanFromContext(
  otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header)),
 )
 defer span.End()

 time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)
}

同樣的部署到集羣中

kubectl apply -f https://raw.githubusercontent.com/togettoyou/jaeger-example/master/jaeger-example-opentelemetry.yaml -n observability

訪問後觀察 Jaeger UI 可以發現調用鏈和之前的也一致,包含 5 個 Span

具體 Span 信息

[1]

官網: https://opentracing.io/

[2]

cncf: https://www.cncf.io/archived-projects/

[3]

參考: https://www.cncf.io/blog/2021/08/26/opentelemetry-becomes-a-cncf-incubating-project/

[4]

參考: https://github.com/open-telemetry/docs-cn/blob/main/OT.md

[5]

OpenTelemetry 規範文檔: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md

[6]

opentelemetry-go-contrib: https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/instrumentation/README.md

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/lriEqTo6-f8Kn1yXeGyP5Q