golang 链路追踪的实现示例
目录
- 问题
- 解决方法
- 链路追踪系统技术选型
- 安装jaeger
- 架构
- 添加jaeger
- 发送span
- 嵌套span
- grpc使用jaeger
- gin使用jaeger
分布式链路追踪(Distributed Tracing),也叫分布式链路跟踪,分布式跟踪,分布式追踪等等。
问题
场景1:调用链过长,查询很慢
web-gin由A负责,服务A由B负责,某个接口出现异常了,先查询日志看是哪个地方出错了,发现服务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、.Nhttp://www.devze.comet Core、Nodejs、PHP、python |
存储 | ES、mysql、Cassandra内存 | ES、kafka、Cassandra内存 | ES、H2、mysql、TIDB、shard sphere |
传输协议支持 | http、MQ | udp/http | gRPC |
ui丰富程度 | 低 | 中 | 中 |
实现方式-代码侵入式 | 拦截请求,侵入 | 拦截请求,侵入 | 字节码注入,无侵入 |
扩展性 | 高 | 高 | 中 |
trace查询 | 支持 | 支持 | 支持 |
性能损失 | 中 | 中 | 低 |
选择jaeger:官方支持jaeger | |||
技术选型原则: | |||
客户端支持、综合考虑、什么语言开发的 |
安装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 - 它是一个监听在 UDP 端口上接收 span 数据的网络守护进程,它会将数据批量发送给 collector。它被设计成一个基础组件,部署到所有的宿主机上。Agent 将 client library 和 collector 解耦,为 client library 屏蔽了路由和发现 collector 的细节。2.Collector - 接收 jaeger-agent 发送来的数据,然后将数据写入后端存储。Collector 被设计成无状态的组件,因此您可以同时运行任意数量的 jaeger-collector。3.Data Store - 后端存储被设计成一个可插拔的组件,支持将数据写入 cassandra、elastic search。4.Query - 接收查询请求,然后从后端存储系统中检索 trace 并通过 UI 进行展示。 Query 是无状态的,您可以启动多个实例,把它们部署在android nginx 这样的负载均衡器后面。分布式追踪系统发展很快,种类繁多,但核心步骤一般有三个:代码埋点,数据存储、查询展示订单调用过程添加jaeger
sudo go get github.com/jaegertracing/jaeger-client-go
发送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/c编程onfig" ) 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 ( android "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{ Typejavascript: 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 链路追踪内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论