golang?鏈路追蹤的實現示例
分布式鏈路追蹤(Distributed Tracing),也叫分布式鏈路跟蹤,分布式跟蹤,分布式追蹤等等。
問題
場景1:調用鏈過長,查詢很慢
web-gin由A負責,服務A由B負責,某個接口出現異常了,先查詢日志看是哪個地方出錯了,發(fā)現服務A調用失敗,依次找到D
場景2:接口相應很慢
解決方法
(1)打日志:慢(可能不看懂其他人寫的日志)
(2)ELK:可以解決,也是日志需要寫入
(3)第三方鏈路追蹤系統可以解決
鏈路追蹤系統技術選型
java使用zipkin和skywalking比較多,而go使用jaeger多
zipkin | jaeger | skywalking | |
---|---|---|---|
OpenTracing | 是 | 是 | 是 |
客戶端支持語言 | java、c#、php、python等 | java、c#、go、php、python等 | java、.Net Core、NodeJS、PHP、python |
存儲 | ES、Mysql、Cassandra內存 | ES、kafka、Cassandra內存 | ES、H2、mysql、TIDB、shard sphere |
傳輸協議支持 | http、MQ | udp/http | gRPC |
ui豐富程度 | 低 | 中 | 中 |
實現方式-代碼侵入式 | 攔截請求,侵入 | 攔截請求,侵入 | 字節(jié)碼注入,無侵入 |
擴展性 | 高 | 高 | 中 |
trace查詢 | 支持 | 支持 | 支持 |
性能損失 | 中 | 中 | 低 |
選擇jaeger:官方支持jaeger | |||
技術選型原則: | |||
客戶端支持、綜合考慮、什么語言開發(fā)的 |
安裝jaeger
docker run \ --rm \ --name jaeger \ -p6831:6831/udp \ -p16686:16686 \ jaegertracing/all-in-one:latest
架構
Jaeger組成
1.Jaeger Client - 為不同語言實現了符合 OpenTracing 標準的 SDK。應用程序通過 API 寫入數據,client library 把 trace 信息按照應用程序指定的采樣策略傳遞給 jaeger-agent。
2.Agent - 它是一個監(jiān)聽在 UDP 端口上接收 span 數據的網絡守護進程,它會將數據批量發(fā)送給 collector。它被設計成一個基礎組件,部署到所有的宿主機上。Agent 將 client library 和 collector 解耦,為 client library 屏蔽了路由和發(fā)現 collector 的細節(jié)。2.Collector - 接收 jaeger-agent 發(fā)送來的數據,然后將數據寫入后端存儲。Collector 被設計成無狀態(tài)的組件,因此您可以同時運行任意數量的 jaeger-collector。
3.Data Store - 后端存儲被設計成一個可插拔的組件,支持將數據寫入 cassandra、elastic search。
4.Query - 接收查詢請求,然后從后端存儲系統中檢索 trace 并通過 UI 進行展示。 Query 是無狀態(tài)的,您可以啟動多個實例,把它們部署在 nginx 這樣的負載均衡器后面。
分布式追蹤系統發(fā)展很快,種類繁多,但核心步驟一般有三個:代碼埋點,數據存儲、查詢展示訂單調用過程
添加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 服務的地址和端口 }, } 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())
到此這篇關于golang 鏈路追蹤的實現示例的文章就介紹到這了,更多相關golang 鏈路追蹤內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Golang中println和fmt.Println區(qū)別解析
Golang 中打印數據通常使用 fmt.Println() 方法,也可以使用內置的 println() 方法。這兩個方法大家可能都使用過,它們的區(qū)別是什么呢?本文給大家詳細講解,感興趣的朋友跟隨小編一起看看吧2023-03-03