gin集成jaeger中间件实现链路追踪
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-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
本文完
参考文档
- jaeger tracing document
- https://chenquan.me/posts/tracing-system-analysis/
- https://github.com/lisw358/tts_otgrpc
- https://blog.51cto.com/u_15481067/11749036
如有侵权,烦请联系删除
相关文章:
gin集成jaeger中间件实现链路追踪
1. 背景 新业务线带来新项目启动,需要改进原有项目的基础框架和组件能力,以提升后续开发和维护效率。项目搭建主要包括技术选型、框架搭建、基础服务搭建等。这其中就涉及到链路追踪的内容,结合其中的踩坑情况,用一篇文章来说明完…...
前端层面----监控与埋点
前言: 站在产品的视角,经常会问如下几个问题: 产品有没有用户使用 用户用得怎么样 系统会不会经常出现异常 如何更好地满足用户需求服务用户 当站在技术视角时,经常会问如下几个问题: 系统出现异常的频率如何 异常…...
linux Command
linux Command 1. 系统监控命令 1.1 top top [param] top -H -p pid,查看进程pid下面的子线程。-b以处理模式操作-c显示完整的命令行而不只是显示命令名。-d 屏幕刷新间隔时间。-l 忽略失效过程。-s 保密模式。-S 累积模式。-u 【用户名】 指定用户名。-p 【进程…...
uniapp登录页面( 适配:pc、小程序、h5)
<!-- 简洁登录页面 --> <template><view class"login-bg"><image class"img-a" src"https://zhoukaiwen.com/img/loginImg/2.png"></image><image class"img-b" src"https://zhoukaiwen.com/im…...
关于OceanBase 多模一体化的浅析
在当今多元化的业务生态中,各行各业对数据库系统的需求各有侧重。举例来说,金融风控领域对数据库的高效事务处理(TP)和分析处理(AP)能力有着严格要求;游戏行业则更加注重文档数据库的灵活性和性…...
快速git
下载 sudo apt install git配置 $ git config --global user.name "John Doe" $ git config --global user.email johndoeexample.com没有空格可以不加双引号如果~/.ssh没有先创建(下一步用) ssh方式制作密钥 github解释 #以邮箱作为标签…...
欺诈文本分类检测(十四):GPTQ量化模型
1. 引言 量化的本质:通过将模型参数从高精度(例如32位)降低到低精度(例如8位),来缩小模型体积。 本文将采用一种训练后量化方法GPTQ,对前文已经训练并合并过的模型文件进行量化,通…...
2024.9.14(RC和RS)
一、replicationcontroller (RC) 1、更改镜像站 [rootk8s-master ~]# vim /etc/docker/daemon.json {"registry-mirrors": ["https://do.nark.eu.org","https://dc.j8.work","https://docker.m.daocloud.io",&…...
【算法随想录04】KMP 字符串匹配算法
这是字符串模式匹配经典算法。 给定一个文本 t 和一个字符串 s,我们尝试找到并展示 s 在 t 中的所有出现(occurrence)。 #include<bits/stdc.h>using namespace std;vector<int> KMP(string s) {int n s.size();vector<int&g…...
TCP和MQTT通信协议
协议分层 网络分层 协议应用层 Co AP MQTT HTTP传输层 UDP TCP网络层 IP链路层 Enternet 网络分层中最…...
Python Pickle 与 JSON 序列化详解:存储、反序列化与对比
Python Pickle 与 JSON 序列化详解:存储、反序列化与对比 文章目录 Python Pickle 与 JSON 序列化详解:存储、反序列化与对比一 功能总览二 Pickle1 应用2 序列化3 反序列化4 系统资源对象1)不能被序列化的系统资源对象2)强行序列…...
第二百三十二节 JPA教程 - JPA教程 - JPA ID自动生成器示例、JPA ID生成策略示例
JPA教程 - JPA ID自动生成器示例 我们可以将id字段标记为自动生成的主键列。 数据库将在插入时自动为id字段生成一个值数据到表。 例子 下面的代码来自Person.java。 package cn.w3cschool.common;import javax.persistence.Entity; import javax.persistence.GeneratedValu…...
计算机网络 ---- 计算机网络的体系结构【计算机网络的分层结构】
一、以快递网络来引入分层思想 1.1 “分层” 的设计思想【将庞大而复杂的问题,转化为若干较小的局部问题】 从我们最熟悉的快递网络出发,在你家附近会有一个快递终点站A,在其他的城市,也会有这种快递终点站,比如说快递…...
Vite + Electron 时,Electron 渲染空白,静态资源加载错误等问题解决
问题 如果在 electron 里直接引入 vite 打包后的东西,那么有些资源是请求不到的 这是我的引入方式 根据报错,我们来到 vite 打包后的路径看一看 ,修改一下 dist 里的文件路径试了一试 修改后的样子,发现是可以的了 原因分析 …...
ZAB协议(算法)
一、ZAB(ZooKeeper Atomic Broadcast)介绍 ZAB 即 ZooKeeper Atomic Broadcast,是 ZooKeeper 实现分布式数据一致性的核心算法。它是一种原子广播协议,用于确保在分布式环境中,多个 ZooKeeper 服务器之间的数据一致性。…...
多个音频怎么合并?把多个音频合并在一起的方法推荐
多个音频怎么合并?无论是制作连贯的播客节目还是将音乐片段整合成专辑,音频合并已成为许多创作者的常见需求。通过有效合并音频,可以显著提升项目的整体质量,确保内容的连续性和一致性。然而,合并后的文件通常比原始单…...
【Django】Django Class-Based Views (CBV) 与 DRF APIView 的区别解析
Django Class-Based Views (CBV) 与 DRF APIView 的区别解析 在 Django 开发中,基于类的视图(Class-Based Views, CBV)是实现可重用性和代码结构化的利器。而 Django REST Framework (DRF) 提供的 APIView 是针对 API 开发的扩展。 一、CBV …...
如何增加Google收录量?
想增加Google收录量,首先自然是你的页面数量就要多,但这些页面的内容也绝对不能敷衍,你的网站都没多少页面,谷歌哪怕想收录都没办法,当然,这是一个过程,持续缓慢的增加页面,增加网站…...
leetcode练习 格雷编码
n 位格雷码序列 是一个由 2n 个整数组成的序列,其中: 每个整数都在范围 [0, 2n - 1] 内(含 0 和 2n - 1)第一个整数是 0一个整数在序列中出现 不超过一次每对 相邻 整数的二进制表示 恰好一位不同 ,且第一个 和 最后一…...
【LLM:Gemini】文本摘要、信息提取、验证和纠错、重新排列图表、视频理解、图像理解、模态组合
开始使用Gemini 目录 开始使用Gemini Gemini简介 Gemini实验结果 Gemini的多模态推理能力 文本摘要 信息提取 验证和纠错 重新排列图表 视频理解 图像理解 模态组合 Gemini多面手编程助理 库的使用 引用 本文概述了Gemini模型和如何有效地提示和使用这些模型。本…...
Phi-4-reasoning-vision-15B入门必看:视觉推理模型prompt工程要点
Phi-4-reasoning-vision-15B入门必看:视觉推理模型prompt工程要点 如果你刚接触Phi-4-reasoning-vision-15B,可能会发现一个奇怪的现象:有时候它像个博学的学者,能精准分析复杂的图表;有时候却像个固执的程序员&#…...
PVE中使用SPICE功能遇到的10个高频率问题和解答方法
SPICE(Simple Protocol for Independent Computing Environments)是PVE(Proxmox VE)虚拟机中一款高效的远程桌面协议,相比默认的VNC,它具备更高的画面流畅度、更低的延迟,还支持文件夹共享、音频传输、USB设备重定向等增强功能,是…...
BC7215红外编解码芯片:协议无关的物理层信号处理方案
1. 项目概述BC7215 是一款高度集成的 8 引脚通用红外遥控信号编解码芯片,专为嵌入式系统设计,具备双向通信能力——既可作为红外接收器(Decoder)解析来自各类遥控器的调制信号,也可作为红外发射器(Encoder&…...
从标注到训练:手把手教你用Labelme制作YOLOv8-Pose可用的关键点数据集
从标注到训练:手把手教你用Labelme制作YOLOv8-Pose可用的关键点数据集 在计算机视觉领域,关键点检测技术正逐渐成为研究热点。无论是人体姿态估计、面部表情识别还是工业质检中的零件定位,准确的关键点检测都是实现这些应用的基础。然而&…...
校正协变量的相关:偏相关分析
当你想研究两个变量(X 和 Y)的关系,但担心其他变量(Z)可能干扰这个关系时,偏相关分析 (Partial Correlation) 可以在剔除协变量的影响后,计算 X 和 Y 之间更“纯粹”的关联。 1. 核心定义 偏相关…...
OpenClaw硬件推荐:流畅运行Kimi-VL-A3B-Thinking的配置清单
OpenClaw硬件推荐:流畅运行Kimi-VL-A3B-Thinking的配置清单 1. 为什么需要关注硬件配置? 去年冬天,当我第一次尝试在MacBook Pro上运行Kimi-VL-A3B-Thinking模型时,风扇的呼啸声让我意识到——多模态模型的硬件需求远比想象中苛…...
从216MB到19MB:某头部智能网关固件编译瘦身全过程(含patch文件与CI/CD集成checklist)
第一章:边缘计算 C 轻量化编译方法概览在资源受限的边缘设备(如工业网关、嵌入式摄像头、车载ECU)上部署C应用,传统编译流程常导致二进制体积臃肿、启动延迟高、内存占用超标。轻量化编译并非简单裁剪功能,而是围绕**目…...
CMPS12磁力计寄存器级驱动与KRAI架构嵌入式实践
CMPS_KRAInew:基于KRAI架构的CMPS12磁力计寄存器级驱动解析与嵌入式集成实践1. 项目概述CMPS_KRAInew 是一个面向嵌入式平台、专为 CMPS12 数字罗盘模块设计的轻量级底层驱动库,其核心定位并非通用 HAL 封装,而是聚焦于 KRAI(Kern…...
2026.4.7总结
工作日精进:这个月在心声上看到许多离职的帖子,估计是有很多拿完年终奖离职的。看到别人写的离职感悟,我多少有些共情。当有一天,我离职的时候,我也要写一篇长篇大论。早上HR跟入职一两年的人解读了esop相关政策。这政…...
单线级联可寻址七段数码管设计
1. 项目概述可寻址七段数码管显示模块(Addressable Seven Segment Display)是一种突破传统驱动架构的嵌入式显示解决方案。其核心设计目标是:仅需单根 GPIO 引脚,即可级联驱动任意数量的七段数码管单元。该方案彻底摒弃了传统数码…...
