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.xdnf.cn/news/141333.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

高通Liunx 系统镜像编译

本文将会介绍如何在编译高通Liunx代码, 具体可以在高通 Linux | 高通下查看相关信息。 编译服务器配置 首先,准备一台Ubuntu 22.04版本主机或者服务器 1,编译Yocto 系统,需要如下一些配置 sudo apt update sudo apt install repo gawk wg…

C++——STL(list类)

1.list的介绍 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。 2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素…

音视频入门基础:AAC专题(5)——FFmpeg源码中,判断某文件是否为AAC裸流文件的实现

一、引言 通过FFmpeg命令: ./ffmpeg -i XXX.aac 可以判断出某个文件是否为AAC裸流文件: 所以FFmpeg是怎样判断出某个文件是否为AAC裸流文件呢?它内部其实是通过adts_aac_probe函数来判断的。从《FFmpeg源码:av_probe_input_for…

TCP socket

TCP的socket和UDP大同小异,基本的代码结构都是相同的。一些相同的接口本文就不赘述了,例如,socket,bind,有需要看这篇文章UDP socket 服务端server 两步:初始化服务端,运行服务端 初始化服务端 创建soc…

物品识别——基于python语言

目录 1.物品识别 2.模型介绍 3.文件框架 4.代码示例 4.1 camera.py 4.2 interaction.py 4.3 object_detection.py 4.4 main.py 4.5 运行结果 5.总结 1.物品识别 该项目使用Python,OpenCV进行图像捕捉,进行物品识别。我们将使用YOLO&#xff08…

人工智能——猴子摘香蕉问题

一、实验目的 求解猴子摘香蕉问题,根据猴子不同的位置,求解猴子的移动范围,求解对应的过程,针对不同的目标状态进行求解。 二、实验内容 根据场景有猴子、箱子、香蕉,香蕉挂天花板上。定义多种谓词描述位置、状态等…

Vue生命周期;Vue路由配置;vue网络请求;vue跨域处理

一&#xff0c;Vue生命周期 <template><div > <h1 click"changeText">{{ info }}</h1></div> </template><script> export default {name: HelloWorld,data(){return{info:"介绍组件生命周期"}},methods:{chang…

CenterNet官方代码—目标检测模型推理部分

CenterNet模型推理部分解析 CenterNet官方代码环境部署 CenterNet作为2019年CVPR推出的论文&#xff0c;论文中给出了官方代码所在的github仓库地址。https://github.com/xingyizhou/CenterNet。 整个代码的代码量并不是特别大&#xff0c;但整个项目的难点在于使用了老版本的…

Djourney新手入门基础,AI摄影+AI设计+AI绘画-AIGC作图

人工智能技术的飞速发展&#xff0c;AI正逐渐渗透进创意领域&#xff0c;特别是在摄影、设计和绘画方面&#xff0c;AIGC&#xff08;Artificial Intelligence for Generative Content&#xff09;技术正在重塑我们的创作方式。本文将深入探讨Djourney这款创新工具&#xff0c;…

XML_Tomcat_HTTP

第四章 XML_Tomcat10_HTTP 一 XML XML是EXtensible Markup Language的缩写&#xff0c;翻译过来就是可扩展标记语言。所以很明显&#xff0c;XML和HTML一样都是标记语言&#xff0c;也就是说它们的基本语法都是标签。 可扩展 三个字表面上的意思是XML允许自定义格式。但这不代…

推荐这款神器:Perplexity

今天推荐是一款AI搜索引擎&#xff0c;还支持gpt-4模型的使用&#xff0c;虽然4小时只能使用5次&#xff0c;但是相比于常规的搜索引擎&#xff0c;在某些方面还是很强的&#xff0c;个人感觉优于newbing。 页面简洁&#xff0c;没有广告&#xff0c;内容丰富&#xff0c;功能…

LinkedHashMap 如何实现排序

目录 一、LinkedHashMap二、排序实现三、代码片段分析 一、LinkedHashMap LinkedHashMap 是 Java 中的一个集合类&#xff0c;它是 HashMap 的一个子类&#xff0c;继承了 HashMap 的所有特性&#xff0c;并且在此基础上增加了一个双向链表来维护元素的插入顺序或者访问顺序。L…

Day26_0.1基础学习MATLAB学习小技巧总结(26)——数据插值

利用空闲时间把碎片化的MATLAB知识重新系统的学习一遍&#xff0c;为了在这个过程中加深印象&#xff0c;也为了能够有所足迹&#xff0c;我会把自己的学习总结发在专栏中&#xff0c;以便学习交流。 参考书目&#xff1a; 1、《MATLAB基础教程 (第三版) (薛山)》 2、《MATL…

C++ STL中sort函数

STL的sort算法&#xff0c;数据量大时采用QuickSort快排算法&#xff0c;分段归并排序。一旦分段后的数据量小于某个门槛&#xff08;16&#xff09;&#xff0c;为避免QuickSort快排的递归调用带来过大的额外负荷&#xff0c;就改用Insertion Sort插入排序。如果递归层次过深&…

C++入门基础知识69(高级)——【关于C++ 动态内存】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于C 动态内存的相关内容&#xff01; 目录…

[产品管理-19]:NPDP新产品开发 - 17 - 产品设计与开发工具 - 实体化设计工具:联合分析、功能分析、FAST技术图和逆向工程

目录 前言&#xff1a; 一、什么是实体化设计 1.1 什么是实体化设计 1、定义与概述 2、设计流程 3、关键要素 4、应用领域 5、举例说明 1.2 实体化设计与概念设计的区别 实体化设计 概念设计 区别归纳 1.3 实体化设计与初步设计、规格设计的区别 1、定义与目的 …

51单片机-LCD1602(液晶显示屏)- 写驱动

时间永远是检验真理唯一标准&#xff01;Whappy&#xff01; 主要简单写出几个驱动 初始化、显示字符、显示字符串、显示整形数据、有符号数据、十六进制、二进制&#xff01; void LCD_Init(); void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char); vo…

平安养老险阜阳中心支公司开展金融教育宣传专项活动

为全面深入开展“金融教育宣传月”的各项工作&#xff0c;不断完善金融惠民利民举措&#xff0c;提升金融服务质效&#xff0c;帮助基层群众增强维权意识、防非反诈的自我保护能力。近日&#xff0c;平安养老保险股份有限公司&#xff08;以下“平安养老险”&#xff09;阜阳中…

【Node.js】RabbitMQ 延时消息

概述 在 RabbitMQ 中实现延迟消息通常需要借助插件&#xff08;如 RabbitMQ 延迟队列插件&#xff09;&#xff0c;因为 RabbitMQ 本身不原生支持延迟消息。 延迟消息的一个典型场景是&#xff0c;当消息发布到队列后&#xff0c;等待一段时间再由消费者消费。这可以通过配置…

带你0到1之QT编程:十一、掌握Containers容器艺术,一网打尽开发利器

此为QT编程的第十一谈&#xff01;关注我&#xff0c;带你快速学习QT编程的学习路线&#xff01; 每一篇的技术点都是很很重要&#xff01;很重要&#xff01;很重要&#xff01;但不冗余&#xff01; 我们通常采取总-分-总和生活化的讲解方式来阐述一个知识点&#xff01; …