当前位置: 首页 > 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

如有侵权,烦请联系删除

相关文章:

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 多模一体化的浅析

在当今多元化的业务生态中&#xff0c;各行各业对数据库系统的需求各有侧重。举例来说&#xff0c;金融风控领域对数据库的高效事务处理&#xff08;TP&#xff09;和分析处理&#xff08;AP&#xff09;能力有着严格要求&#xff1b;游戏行业则更加注重文档数据库的灵活性和性…...

快速git

下载 sudo apt install git配置 $ git config --global user.name "John Doe" $ git config --global user.email johndoeexample.com没有空格可以不加双引号如果~/.ssh没有先创建&#xff08;下一步用&#xff09; ssh方式制作密钥 github解释 #以邮箱作为标签…...

欺诈文本分类检测(十四):GPTQ量化模型

1. 引言 量化的本质&#xff1a;通过将模型参数从高精度&#xff08;例如32位&#xff09;降低到低精度&#xff08;例如8位&#xff09;&#xff0c;来缩小模型体积。 本文将采用一种训练后量化方法GPTQ&#xff0c;对前文已经训练并合并过的模型文件进行量化&#xff0c;通…...

2024.9.14(RC和RS)

一、replicationcontroller &#xff08;RC&#xff09; 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&#xff0c;我们尝试找到并展示 s 在 t 中的所有出现&#xff08;occurrence&#xff09;。 #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 序列化详解&#xff1a;存储、反序列化与对比 文章目录 Python Pickle 与 JSON 序列化详解&#xff1a;存储、反序列化与对比一 功能总览二 Pickle1 应用2 序列化3 反序列化4 系统资源对象1&#xff09;不能被序列化的系统资源对象2&#xff09;强行序列…...

第二百三十二节 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 “分层” 的设计思想【将庞大而复杂的问题&#xff0c;转化为若干较小的局部问题】 从我们最熟悉的快递网络出发&#xff0c;在你家附近会有一个快递终点站A&#xff0c;在其他的城市&#xff0c;也会有这种快递终点站&#xff0c;比如说快递…...

Vite + Electron 时,Electron 渲染空白,静态资源加载错误等问题解决

问题 如果在 electron 里直接引入 vite 打包后的东西&#xff0c;那么有些资源是请求不到的 这是我的引入方式 根据报错&#xff0c;我们来到 vite 打包后的路径看一看 &#xff0c;修改一下 dist 里的文件路径试了一试 修改后的样子&#xff0c;发现是可以的了 原因分析 …...

ZAB协议(算法)

一、ZAB&#xff08;ZooKeeper Atomic Broadcast&#xff09;介绍 ZAB 即 ZooKeeper Atomic Broadcast&#xff0c;是 ZooKeeper 实现分布式数据一致性的核心算法。它是一种原子广播协议&#xff0c;用于确保在分布式环境中&#xff0c;多个 ZooKeeper 服务器之间的数据一致性。…...

多个音频怎么合并?把多个音频合并在一起的方法推荐

多个音频怎么合并&#xff1f;无论是制作连贯的播客节目还是将音乐片段整合成专辑&#xff0c;音频合并已成为许多创作者的常见需求。通过有效合并音频&#xff0c;可以显著提升项目的整体质量&#xff0c;确保内容的连续性和一致性。然而&#xff0c;合并后的文件通常比原始单…...

【Django】Django Class-Based Views (CBV) 与 DRF APIView 的区别解析

Django Class-Based Views (CBV) 与 DRF APIView 的区别解析 在 Django 开发中&#xff0c;基于类的视图&#xff08;Class-Based Views, CBV&#xff09;是实现可重用性和代码结构化的利器。而 Django REST Framework (DRF) 提供的 APIView 是针对 API 开发的扩展。 一、CBV …...

如何增加Google收录量?

想增加Google收录量&#xff0c;首先自然是你的页面数量就要多&#xff0c;但这些页面的内容也绝对不能敷衍&#xff0c;你的网站都没多少页面&#xff0c;谷歌哪怕想收录都没办法&#xff0c;当然&#xff0c;这是一个过程&#xff0c;持续缓慢的增加页面&#xff0c;增加网站…...

leetcode练习 格雷编码

n 位格雷码序列 是一个由 2n 个整数组成的序列&#xff0c;其中&#xff1a; 每个整数都在范围 [0, 2n - 1] 内&#xff08;含 0 和 2n - 1&#xff09;第一个整数是 0一个整数在序列中出现 不超过一次每对 相邻 整数的二进制表示 恰好一位不同 &#xff0c;且第一个 和 最后一…...

【LLM:Gemini】文本摘要、信息提取、验证和纠错、重新排列图表、视频理解、图像理解、模态组合

开始使用Gemini 目录 开始使用Gemini Gemini简介 Gemini实验结果 Gemini的多模态推理能力 文本摘要 信息提取 验证和纠错 重新排列图表 视频理解 图像理解 模态组合 Gemini多面手编程助理 库的使用 引用 本文概述了Gemini模型和如何有效地提示和使用这些模型。本…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …...

多元隐函数 偏导公式

我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式&#xff0c;给定一个隐函数关系&#xff1a; F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 &#x1f9e0; 目标&#xff1a; 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z​、 …...

goreplay

1.github地址 https://github.com/buger/goreplay 2.简单介绍 GoReplay 是一个开源的网络监控工具&#xff0c;可以记录用户的实时流量并将其用于镜像、负载测试、监控和详细分析。 3.出现背景 随着应用程序的增长&#xff0c;测试它所需的工作量也会呈指数级增长。GoRepl…...

用js实现常见排序算法

以下是几种常见排序算法的 JS实现&#xff0c;包括选择排序、冒泡排序、插入排序、快速排序和归并排序&#xff0c;以及每种算法的特点和复杂度分析 1. 选择排序&#xff08;Selection Sort&#xff09; 核心思想&#xff1a;每次从未排序部分选择最小元素&#xff0c;与未排…...

Python异步编程:深入理解协程的原理与实践指南

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 持续学习&#xff0c;不断…...