可视化建站源码,游戏分类网站怎么做,百度应用中心,用什么软件做网站最简单 最方便1. 背景
新业务线带来新项目启动#xff0c;需要改进原有项目的基础框架和组件能力#xff0c;以提升后续开发和维护效率。项目搭建主要包括技术选型、框架搭建、基础服务搭建等。这其中就涉及到链路追踪的内容#xff0c;结合其中的踩坑情况#xff0c;用一篇文章来说明完…1. 背景
新业务线带来新项目启动需要改进原有项目的基础框架和组件能力以提升后续开发和维护效率。项目搭建主要包括技术选型、框架搭建、基础服务搭建等。这其中就涉及到链路追踪的内容结合其中的踩坑情况用一篇文章来说明完整的链路搭建过程。
2. 技术选型
2.1 方案对比
图【1】来自网络请自行对比验证 图 1 2.2 选型
本项目基于golang和gin框架以及链路中间件对比选择jaeger作为工具进行集成。
3. 核心实现
3.1 jaeger服务搭建
这里就借助网上的all-in-one的docker方式搭建即可无需多费力。
docker run --rm --name jaeger \-e COLLECTOR_ZIPKIN_HOST_PORT:9411 \-p 6831:6831/udp \-p 6832:6832/udp \-p 5778:5778 \-p 16686:16686 \-p 4317:4317 \-p 4318:4318 \-p 14250:14250 \-p 14268:14268 \-p 14269:14269 \-p 9411:9411 \jaegertracing/all-in-one:1.60
这里启动了很多端口详细的自查即可这里两个比较重要的端口如下 16686服务前端端口ip:port 可访问UI 6831项目中连接端口 jaeger界面图【2】所示。 图 2 3.2 protoc编译器及语言插件安装
protobuf协议是一种跨语言、跨平台的数据结构序列化和反序列化框架类似于thrift协议 1 编写 .proto ⽂件⽬的是为了定义结构对象message及属性内容。 2 使⽤ protoc 编译器编译 .proto ⽂件⽣成⼀系列接⼝代码存放在新⽣成头⽂件和源⽂件中。 3 依赖⽣成的接⼝将编译⽣成的头⽂件包含进我们的代码中实现对 .proto ⽂件中定义的字段进行设置和获取和对 message 对象进行序列化和反序列化 安装
go install google.golang.org/protobuf/cmd/protoc-gen-golatest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpclatest编写proto文件
syntax proto3;option go_package ./proto;
package helloworld;service Greeter {rpc SayHello (HelloReq) returns (HelloResp);
}message HelloReq {string name 1;
}message HelloResp {string message 1;
}编译proto
protoc --go_out ../ helloword.proto3.2 项目实现
项目架构是上层服务http下层服务rpc选用protobuf协议跨语言、跨平台的数据结构序列化和反序列化框架类似于thrift协议作为就以简单的helloword为例实现。 核心插件jaeger、opentracing和otgrpc
在上层增加中间件并注册到engine上。
package middlewareimport (github.com/gin-gonic/gingithub.com/opentracing/opentracing-gogithub.com/uber/jaeger-client-gojaegercfg github.com/uber/jaeger-client-go/config
)func Trace() gin.HandlerFunc {return func(ctx *gin.Context) {cfg : jaegercfg.Configuration{ServiceName: demo, //服务名称Sampler: jaegercfg.SamplerConfig{Type: jaeger.SamplerTypeConst,Param: 1,},Reporter: jaegercfg.ReporterConfig{LogSpans: true,LocalAgentHostPort: xxxx:6831, //jaeger 服务器ip},}tracer, closer, err : cfg.NewTracer()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()}
}// rpc调用设置globaltracer
consul : ServerConfig.Consul
conn, err : grpc.Dial(fmt.Sprintf(consul://%s:%s/%s?wait14s, consul.Host, consul.Port, ServerName),grpc.WithTransportCredentials(insecure.NewCredentials()),grpc.WithDefaultServiceConfig({loadBalancingPolicy: round_robin}),grpc.WithUnaryInterceptor(otgrpc.OpenTracingClientInterceptor(opentracing.GlobalTracer())), //这里把全局tracer传下去
)// 注册路由中间件
engine.Use(middleware.Trace())启动项目访问就可以在jaeger上看到链路信息。 图 3 点击进入详情页查看信息这时候大家发现一个问题这里只有http层的调用信息没有rpc层这也是网上所有介绍的文档里没有说明白的一点。 图 4 真正原因是中间件代码里的tracer和parentSpan没有传递下去。
startSpan : tracer.StartSpan(ctx.Request.URL.Path)
defer startSpan.Finish()
ctx.Set(tracer, tracer)
ctx.Set(parentSpan, startSpan) // 虽然设置了没有跟随rpc传下去
ctx.Next()怎么传递呢这里是关键 首先是在rpc服务发现时传递全局tracer
#引入传递tracer的包
go get github.com/lisw358/tts_otgrpc源码里有一个地方别人做了改动来实现tracer传递改动如下
//自己改源码部分开始
//获取到父span和trace
//此处需要在ctx上有这个key来向下传递tracer和parentSpan
ginConetext : ctx.Value(ginContext)
switch ginConetext.(type) {
case *gin.Context://获取traceif itrace, ok : ginConetext.(*gin.Context).Get(tracer); ok {tracer itrace.(opentracing.Tracer)}//获取父spanif parentSpan, ok : ginConetext.(*gin.Context).Get(parentSpan); ok {parentCtx parentSpan.(*jaeger.Span).Context()}
}
//自己改源码部分结束因此全局定义一个ContextWapper来增加ginContext的设置 func ContextWrapper(c *gin.Context) context.Context {return context.WithValue(context.Background(), GinContext, c)
}然后在调用时处理一下context即可
req : proto.HelloReq{}
req.Name c.DefaultQuery(name, guest)//这里包装了一下context来保证otgrpc包里能正常判断ginContext变量来生成子span
res, _ : services.SayHello(global.ContextWrapper(c), req)
c.JSON(0, res.Message)
jaeger链路追踪结果如图【5】所示 图 5 本文完
参考文档 jaeger tracing documenthttps://chenquan.me/posts/tracing-system-analysis/https://github.com/lisw358/tts_otgrpchttps://blog.51cto.com/u_15481067/11749036 如有侵权烦请联系删除