当前位置: 首页 > news >正文

gin集成jaeger中间件实现链路追踪

1. 背景

新业务线带来新项目启动,需要改进原有项目的基础框架和组件能力,以提升后续开发和维护效率。项目搭建主要包括技术选型、框架搭建、基础服务搭建等。这其中就涉及到链路追踪的内容,结合其中的踩坑情况,用一篇文章来说明完整的链路搭建过程。

2. 技术选型

2.1 方案对比

图【1】来自网络,请自行对比验证

图 1

2.2 选型

本项目基于golanggin框架,以及链路中间件对比,选择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-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

编写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.proto

3.2 项目实现

项目架构是上层服务http,下层服务rpc,选用protobuf协议(跨语言、跨平台的数据结构序列化和反序列化框架,类似于thrift协议)作为就以简单的helloword为例实现。
核心插件jaeger、opentracing和otgrpc

在上层增加中间件并注册到engine上。

package middlewareimport ("github.com/gin-gonic/gin""github.com/opentracing/opentracing-go""github.com/uber/jaeger-client-go"jaegercfg "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?wait=14s", 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

本文完

参考文档

  1. jaeger tracing document
  2. https://chenquan.me/posts/tracing-system-analysis/
  3. https://github.com/lisw358/tts_otgrpc
  4. https://blog.51cto.com/u_15481067/11749036

如有侵权,烦请联系删除

http://www.lryc.cn/news/438624.html

相关文章:

  • 前端层面----监控与埋点
  • linux Command
  • uniapp登录页面( 适配:pc、小程序、h5)
  • 关于OceanBase 多模一体化的浅析
  • 快速git
  • 欺诈文本分类检测(十四):GPTQ量化模型
  • 2024.9.14(RC和RS)
  • 【算法随想录04】KMP 字符串匹配算法
  • TCP和MQTT通信协议
  • Python Pickle 与 JSON 序列化详解:存储、反序列化与对比
  • 第二百三十二节 JPA教程 - JPA教程 - JPA ID自动生成器示例、JPA ID生成策略示例
  • 计算机网络 ---- 计算机网络的体系结构【计算机网络的分层结构】
  • Vite + Electron 时,Electron 渲染空白,静态资源加载错误等问题解决
  • ZAB协议(算法)
  • 多个音频怎么合并?把多个音频合并在一起的方法推荐
  • 【Django】Django Class-Based Views (CBV) 与 DRF APIView 的区别解析
  • 如何增加Google收录量?
  • leetcode练习 格雷编码
  • 【LLM:Gemini】文本摘要、信息提取、验证和纠错、重新排列图表、视频理解、图像理解、模态组合
  • CMS之Wordpress建设
  • 使用Neo4j存储聊天记录的简单教程
  • 前端面试常考算法
  • 【机试准备】常用容器与函数
  • Base 社区见面会 | 新加坡站
  • 麒麟操作系统搭建Nacos集群
  • Imagination推出性能最高且具有高等级功能安全性的汽车GPU IP
  • 端口大全说明,HTTP,TCP,UDP常见端口对照表
  • dplyr、tidyverse和ggplot2初探
  • pandas:读取各类文件方法以及爬虫时json数据保存
  • 二、(JS)JS中常见的键盘事件