分布式鏈路追蹤續(xù)集
本文是 分布式鏈路追蹤 的續(xù)集。
在上一文中提到為了統(tǒng)一各種分布式追蹤系統(tǒng)的實(shí)現(xiàn),CNCF (云原生計(jì)算基金會(huì))下的 OpenTracing 項(xiàng)目定義了一套分布式追蹤的標(biāo)準(zhǔn),可以使用 OpenTracing API 完成代碼的監(jiān)控埋點(diǎn),最后用戶自由選擇遵循 OpenTracing 標(biāo)準(zhǔn)的鏈路追蹤系統(tǒng),比如 jaeger 。
但是現(xiàn)在訪問 OpenTracing 的 官網(wǎng)[1] ,可以發(fā)現(xiàn)官網(wǎng)提醒 OpenTracing 和 OpenCensus 已經(jīng)被合并成為 OpenTelemetry 。(在上文編寫時(shí)我未發(fā)現(xiàn)到,感謝 Donald Liu 大佬的提醒)

在 cncf[2] 上也可以發(fā)現(xiàn) OpenTracing 已經(jīng)被 Archived 了

相應(yīng)地,OpenTelemetry 已經(jīng)正式成為了 CNCF 的孵化項(xiàng)目 參考[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) 領(lǐng)域,或者說微服務(wù)的可觀察性方面包括有分布式鏈路追蹤,指標(biāo)監(jiān)控和日志。
OpenTracing 是最早為分布式追蹤制定了一套平臺(tái)無關(guān)、廠商無關(guān)的協(xié)議標(biāo)準(zhǔn)的項(xiàng)目,并以此成為了 CNCF 的孵化項(xiàng)目。
在之后,谷歌牽頭,微軟加入,創(chuàng)建了 OpenCensus 項(xiàng)目統(tǒng)一 Metrics 基礎(chǔ)指標(biāo)監(jiān)控的使用方式,還做了 OpenTracing 的老本行:分布式追蹤。
一山不容二虎,OpenTracing 和 OpenCensus 愈打愈烈,對(duì)我們用戶來講,實(shí)在是太不友好了。
然后 OpenTelemetry 橫空出世了,OpenTracing 和 OpenCensus 既然都這么好,干脆你們合并起來吧,我 OpenTelemetry 來兼容你們。
OpenTelemetry 的自身定位十分明確:數(shù)據(jù)采集和標(biāo)準(zhǔn)規(guī)范的統(tǒng)一,對(duì)于數(shù)據(jù)如何去使用、存儲(chǔ)、展示、告警,官方是不涉及的。
OpenTelemetry 的終極目標(biāo)十分偉大:實(shí)現(xiàn) Metrics、Tracing、Logging 的融合及大一統(tǒng),作為 APM 的數(shù)據(jù)采集終極解決方案。
然后就是現(xiàn)在的故事了,OpenTelemetry 正式成為 CNCF 的孵化項(xiàng)目,OpenTracing 和 OpenCensus 不再維護(hù)。
兼容性
這個(gè)放心,大部分的知識(shí)點(diǎn)定義還是一致的 (如數(shù)據(jù)模型:Trace, Span, SpanContext) ,但是 API 的使用會(huì)有所區(qū)別,詳細(xì)可以查看 OpenTelemetry 規(guī)范文檔[5]
承接上文
我們現(xiàn)在對(duì)上文的 OpenTracing API 的代碼做出修改,擁抱 OpenTelemetry 吧!
安裝 OpenTelemetry for Go 依賴
go?get?go.opentelemetry.io/[email protected]?go.opentelemetry.io/otel/[email protected]?go.opentelemetry.io/otel/exporters/stdout/[email protected]?go.opentelemetry.io/otel/[email protected]
安裝 OpenTelemetry for jaeger 依賴
go?get?-u?go.opentelemetry.io/otel/exporters/jaeger
OpenTelemetry 官方為多種開源框架提供了開箱即用的 Instrumentation Packages ,如 gin , beego , mux , go-kit 等,當(dāng)然也支持 net/http 標(biāo)準(zhǔn)庫 ,更多可瀏覽opentelemetry-go-contrib[6]
我們使用的是 net/http 標(biāo)準(zhǔn)庫,所以直接導(dǎo)入 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"),?//?服務(wù)名
???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),?"請(qǐng)求?/home"))
?mux.Handle("/async",?otelhttp.NewHandler(http.HandlerFunc(serviceHandler),?"請(qǐng)求?/async"))
?mux.Handle("/service",?otelhttp.NewHandler(http.HandlerFunc(serviceHandler),?"請(qǐng)求?/service"))
?mux.Handle("/db",?otelhttp.NewHandler(http.HandlerFunc(dbHandler),?"請(qǐng)求?/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(`?點(diǎn)擊開始發(fā)起請(qǐng)求?`))
}
func?homeHandler(w?http.ResponseWriter,?r?*http.Request)?{
?w.Write([]byte("開始請(qǐng)求...\n"))
?ctx?:=?r.Context()
?span :=?trace.SpanFromContext(ctx)
?defer?span.End()
?//?發(fā)起異步請(qǐng)求
?asyncReq,?_?:=?http.NewRequest("GET",?"http://localhost:8080/async",?nil)
?//?傳遞span的上下文信息
?//?將關(guān)于本地追蹤調(diào)用的span context,設(shè)置到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("請(qǐng)求?/async?error",?err.Error()),
???)
??}
?}()
?time.Sleep(time.Duration(rand.Intn(200))?*?time.Millisecond)
?//?發(fā)起同步請(qǐng)求
?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("請(qǐng)求?/service?error",?err.Error()),
??)
?}
?w.Write([]byte("請(qǐng)求結(jié)束!"))
}
//?模擬業(yè)務(wù)請(qǐng)求
func?serviceHandler(w?http.ResponseWriter,?r?*http.Request)?{
?//?通過http?header,提取span元數(shù)據(jù)信息
?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("請(qǐng)求?/db?error",?err.Error()),
??)
?}
?time.Sleep(time.Duration(rand.Intn(200))?*?time.Millisecond)
}
//?模擬DB調(diào)用
func?dbHandler(w?http.ResponseWriter,?r?*http.Request)?{
?//?通過http?header,提取span元數(shù)據(jù)信息
?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 可以發(fā)現(xiàn)調(diào)用鏈和之前的也一致,包含 5 個(gè) Span

具體 Span 信息


參考資料
官網(wǎng): 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 規(guī)范文檔: 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
