golang?鏈路追蹤的實(shí)現(xiàn)示例
分布式鏈路追蹤(Distributed Tracing),也叫分布式鏈路跟蹤,分布式跟蹤,分布式追蹤等等。
問題
場景1:調(diào)用鏈過長,查詢很慢
web-gin由A負(fù)責(zé),服務(wù)A由B負(fù)責(zé),某個(gè)接口出現(xiàn)異常了,先查詢?nèi)罩究词悄膫€(gè)地方出錯(cuò)了,發(fā)現(xiàn)服務(wù)A調(diào)用失敗,依次找到D
場景2:接口相應(yīng)很慢
解決方法
(1)打日志:慢(可能不看懂其他人寫的日志)
(2)ELK:可以解決,也是日志需要寫入
(3)第三方鏈路追蹤系統(tǒng)可以解決
鏈路追蹤系統(tǒng)技術(shù)選型
java使用zipkin和skywalking比較多,而go使用jaeger多
zipkin | jaeger | skywalking | |
---|---|---|---|
OpenTracing | 是 | 是 | 是 |
客戶端支持語言 | java、c#、php、python等 | java、c#、go、php、python等 | java、.Net Core、NodeJS、PHP、python |
存儲(chǔ) | ES、Mysql、Cassandra內(nèi)存 | ES、kafka、Cassandra內(nèi)存 | ES、H2、mysql、TIDB、shard sphere |
傳輸協(xié)議支持 | http、MQ | udp/http | gRPC |
ui豐富程度 | 低 | 中 | 中 |
實(shí)現(xiàn)方式-代碼侵入式 | 攔截請(qǐng)求,侵入 | 攔截請(qǐng)求,侵入 | 字節(jié)碼注入,無侵入 |
擴(kuò)展性 | 高 | 高 | 中 |
trace查詢 | 支持 | 支持 | 支持 |
性能損失 | 中 | 中 | 低 |
選擇jaeger:官方支持jaeger | |||
技術(shù)選型原則: | |||
客戶端支持、綜合考慮、什么語言開發(fā)的 |
安裝jaeger
docker run \ --rm \ --name jaeger \ -p6831:6831/udp \ -p16686:16686 \ jaegertracing/all-in-one:latest
架構(gòu)
Jaeger組成
1.Jaeger Client - 為不同語言實(shí)現(xiàn)了符合 OpenTracing 標(biāo)準(zhǔn)的 SDK。應(yīng)用程序通過 API 寫入數(shù)據(jù),client library 把 trace 信息按照應(yīng)用程序指定的采樣策略傳遞給 jaeger-agent。
2.Agent - 它是一個(gè)監(jiān)聽在 UDP 端口上接收 span 數(shù)據(jù)的網(wǎng)絡(luò)守護(hù)進(jìn)程,它會(huì)將數(shù)據(jù)批量發(fā)送給 collector。它被設(shè)計(jì)成一個(gè)基礎(chǔ)組件,部署到所有的宿主機(jī)上。Agent 將 client library 和 collector 解耦,為 client library 屏蔽了路由和發(fā)現(xiàn) collector 的細(xì)節(jié)。2.Collector - 接收 jaeger-agent 發(fā)送來的數(shù)據(jù),然后將數(shù)據(jù)寫入后端存儲(chǔ)。Collector 被設(shè)計(jì)成無狀態(tài)的組件,因此您可以同時(shí)運(yùn)行任意數(shù)量的 jaeger-collector。
3.Data Store - 后端存儲(chǔ)被設(shè)計(jì)成一個(gè)可插拔的組件,支持將數(shù)據(jù)寫入 cassandra、elastic search。
4.Query - 接收查詢請(qǐng)求,然后從后端存儲(chǔ)系統(tǒng)中檢索 trace 并通過 UI 進(jìn)行展示。 Query 是無狀態(tài)的,您可以啟動(dòng)多個(gè)實(shí)例,把它們部署在 nginx 這樣的負(fù)載均衡器后面。
分布式追蹤系統(tǒng)發(fā)展很快,種類繁多,但核心步驟一般有三個(gè):代碼埋點(diǎn),數(shù)據(jù)存儲(chǔ)、查詢展示訂單調(diào)用過程
添加jaeger
sudo go get github.com/jaegertracing/jaeger-client-go
發(fā)送span
package main import ( "time" "github.com/uber/jaeger-client-go" jaegercfg "github.com/uber/jaeger-client-go/config" ) func main() { cfg := jaegercfg.Configuration{ Sampler: &jaegercfg.SamplerConfig{ Type: jaeger.SamplerTypeConst, Param: 1, //[0,1] 0不采樣1一直采樣 }, Reporter: &jaegercfg.ReporterConfig{ LogSpans: true, //打不打印日志 LocalAgentHostPort: "192.168.31.19:6831", }, ServiceName: "shop", } tracer, closer, err := cfg.NewTracer(jaegercfg.Logger(jaeger.StdLogger)) if err != nil { panic(err) } defer closer.Close() span := tracer.StartSpan("go-grpc-web") time.Sleep(time.Second) defer span.Finish() }
嵌套span
package main import ( "time" "github.com/opentracing/opentracing-go" "github.com/uber/jaeger-client-go" jaegercfg "github.com/uber/jaeger-client-go/config" ) func main() { cfg := jaegercfg.Configuration{ Sampler: &jaegercfg.SamplerConfig{ Type: jaeger.SamplerTypeConst, Param: 1, //[0,1] 0不采樣1一直采樣 }, Reporter: &jaegercfg.ReporterConfig{ LogSpans: true, //打不打印日志 LocalAgentHostPort: "39.103.59.35:6831", }, ServiceName: "shop", } tracer, closer, err := cfg.NewTracer(jaegercfg.Logger(jaeger.StdLogger)) if err != nil { panic(err) } defer closer.Close() parentSpan := tracer.StartSpan("main") spanA := tracer.StartSpan("funcA", opentracing.ChildOf(parentSpan.Context())) time.Sleep(time.Millisecond * 500) spanA.Finish() spanB := tracer.StartSpan("funcB", opentracing.ChildOf(parentSpan.Context())) time.Sleep(time.Millisecond * 1000) spanB.Finish() parentSpan.Finish() }
grpc使用jaeger
package main import ( "context" "fmt" "japter_test/proto" "japter_test/otgrpc" "github.com/opentracing/opentracing-go" "github.com/uber/jaeger-client-go" jaegercfg "github.com/uber/jaeger-client-go/config" "google.golang.org/grpc" ) func main() { cfg := jaegercfg.Configuration{ ServiceName: "mxshop", Sampler: &jaegercfg.SamplerConfig{ Type: jaeger.SamplerTypeConst, Param: 1, //[0,1] 0不采樣1一直采樣 }, Reporter: &jaegercfg.ReporterConfig{ LogSpans: true, //打不打印日志 LocalAgentHostPort: "39.103.59.35:6831", }, } tracer, closer, err := cfg.NewTracer(jaegercfg.Logger(jaeger.StdLogger)) if err != nil { panic(err) } opentracing.SetGlobalTracer(tracer) defer closer.Close() conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure(), grpc.WithUnaryInterceptor(otgrpc.OpenTracingClientInterceptor(opentracing.GlobalTracer()))) if err != nil { panic(err) } defer conn.Close() c := proto.NewGreeterClient(conn) r, err := c.SayHello(context.Background(), &proto.HelloRquest{ Name: "bobby", }) if err != nil { panic(err) } fmt.Println(r.Message) }
gin使用jaeger
攔截器
package middlewares import ( "GolangStudy/Practice/shop/goods-web/global" "fmt" "github.com/gin-gonic/gin" "github.com/opentracing/opentracing-go" "github.com/uber/jaeger-client-go" jaegerConfig "github.com/uber/jaeger-client-go/config" ) func Trace() gin.HandlerFunc { return func(ctx *gin.Context) { cfg := jaegerConfig.Configuration{ ServiceName: "your_service_name", Sampler: &jaegerConfig.SamplerConfig{ Type: jaeger.SamplerTypeConst, Param: 1, // 1 = always sample }, Reporter: &jaegerConfig.ReporterConfig{ LogSpans: true, LocalAgentHostPort: fmt.Sprintf("%s:%d", global.ServerConfig.JaegerInfo.Host, global.ServerConfig.JaegerInfo.Port), // Jaeger Query 服務(wù)的地址和端口 }, } tracer, closer, err := cfg.NewTracer(jaegerConfig.Logger(jaeger.StdLogger)) if err != nil { panic(err) } opentracing.SetGlobalTracer(tracer) defer closer.Close() startSpan := tracer.StartSpan(ctx.Request.URL.Path) defer startSpan.Finish() ctx.Set("tracer", tracer) ctx.Set("parentSpan", startSpan) ctx.Next() } }
使用
GoodsRouter := Router.Group("goods").Use(middlewares.Trace())
到此這篇關(guān)于golang 鏈路追蹤的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)golang 鏈路追蹤內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go語言實(shí)現(xiàn)簡單的并發(fā)網(wǎng)頁爬蟲示例
在這個(gè)章節(jié)中,我們將會(huì)結(jié)合之前的知識(shí)點(diǎn),實(shí)現(xiàn)一個(gè)簡易的并發(fā)網(wǎng)頁爬蟲,我們的爬蟲會(huì)先爬取一個(gè)起始頁面,提取出所有的鏈接,然后并發(fā)地爬取這些鏈接,有需要的朋友可以借鑒參考下,希望能夠有所幫助2023-08-08Go語言實(shí)現(xiàn)服務(wù)端消息接收和發(fā)送
這篇文章主要為大家詳細(xì)介紹了Go語言實(shí)現(xiàn)服務(wù)端消息接收和發(fā)送功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07Golang中println和fmt.Println區(qū)別解析
Golang 中打印數(shù)據(jù)通常使用 fmt.Println() 方法,也可以使用內(nèi)置的 println() 方法。這兩個(gè)方法大家可能都使用過,它們的區(qū)別是什么呢?本文給大家詳細(xì)講解,感興趣的朋友跟隨小編一起看看吧2023-03-03