Gone v2 Tracer 组件-给微服务提供统一的traceID

ops/2025/3/18 12:21:32/

项目地址:https://github.com/gone-io/gone

原文地址:https://github.com/gone-io/goner/blob/main/tracer/README.md

文章目录

    • 功能特性
    • 安装
    • 快速开始
      • 1. 加载追踪组件
      • 2. 使用追踪功能
    • 实现方式
    • 性能测试
    • API 参考
      • Tracer 接口
    • 高级用法
      • 在HTTP服务中使用
      • 微服务间传递追踪ID
    • 最佳实践
    • 注意事项

gone-tracer 是 Gone 框架的分布式追踪组件,用于提供了统一的trace ID。通过该组件,您可以轻松地在 Gone 应用中实现分布式追踪,跟踪请求在多个服务和多个 goroutine 之间的传递,便于问题排查和性能分析。

功能特性

  • 与 Gone 框架无缝集成
  • 自动生成和传递trace ID
  • 支持跨 goroutine 的trace ID 传递
  • 提供两种实现方式:基于github.com/jtolds/gls和基于github.com/petermattis/goid映射
  • 简化日志关联和请求追踪
  • 轻量级设计,低性能开销

安装

go get github.com/gone-io/goner

快速开始

1. 加载追踪组件

package mainimport ("github.com/gone-io/gone/v2""github.com/gone-io/goner/tracer"
)func main() {gone.Loads(tracer.Load,  // 加载追踪组件// 其他组件...).Run(func() {// 启动应用})
}

2. 使用追踪功能

type MyService struct {gone.Flagtracer tracer.Tracer `gone:"*"`  // 注入追踪器logger gone.Logger   `gone:"*"`  // 注入日志器
}func (s *MyService) DoSomething() {// 设置trace ID 并执行函数s.tracer.SetTraceId("", func() {// 获取当前 goroutine 的trace IDtraceId := s.tracer.GetTraceId()s.logger.Infof("当前trace ID: %s", traceId)// 在新的 goroutine 中保持trace IDs.tracer.Go(func() {// 这里的 GetTraceId() 会返回与父 goroutine 相同的trace IDs.logger.Infof("子 goroutine 的trace ID: %s", s.tracer.GetTraceId())})})
}

实现方式

Gone Tracer 组件提供了两种实现方式:

  1. 基于goroutine本地存储 (tracer):使用 github.com/jtolds/gls 库实现,通过goroutine本地存储机制保存和传递追踪ID。

  2. 基于goroutine ID映射 (tracerOverGid):使用 github.com/petermattis/goid 库获取goroutine ID,并通过sync.Map维护goroutine ID与追踪ID的映射关系。

默认情况下,组件使用第一种实现方式。如果您希望使用基于goroutine ID的实现,可以在加载组件时指定:

tracer.LoadOverGid  // 加载基于goroutine ID的追踪组件

性能测试

goner go test -bench=. -benchmem ./tracer
goos: darwin
goarch: arm64
pkg: github.com/gone-io/goner/tracer
cpu: Apple M1 Pro
BenchmarkTracer_SetTraceId-8             1479470               833.5 ns/op           976 B/op         11 allocs/op
BenchmarkTracerOverGid_SetTraceId-8     17734480                67.41 ns/op           64 B/op          2 allocs/op
BenchmarkTracer_GetTraceId-8             1533403               783.8 ns/op           128 B/op          1 allocs/op
BenchmarkTracerOverGid_GetTraceId-8     120586562                9.443 ns/op           0 B/op          0 allocs/op
BenchmarkTracer_Go-8                      365029              4421 ns/op             987 B/op         12 allocs/op
BenchmarkTracerOverGid_Go-8              1693129               709.2 ns/op           157 B/op          5 allocs/op
BenchmarkTracer_Concurrent-8               30535             39531 ns/op           12665 B/op        148 allocs/op
BenchmarkTracerOverGid_Concurrent-8       252675              4841 ns/op            1222 B/op         41 allocs/op
BenchmarkTracer_Nested-8                  120984              9931 ns/op            2592 B/op         28 allocs/op
BenchmarkTracerOverGid_Nested-8          5440866               221.3 ns/op           168 B/op          7 allocs/op
PASS
ok      github.com/gone-io/goner/tracer 17.094s

API 参考

Tracer 接口

type Tracer interface {// SetTraceId 设置trace ID,如果 traceId 为空字符串,则自动生成一个// 通过回调函数 fn 执行业务逻辑,在 fn 内部可以通过 GetTraceId 获取设置的trace IDSetTraceId(traceId string, fn func())// GetTraceId 获取当前 goroutine 的trace IDGetTraceId() string// Go 启动一个新的 goroutine,并传递当前的trace ID// 这个方法可以替代标准的 go 关键字,确保子 goroutine 能够继承父 goroutine 的trace IDGo(fn func())
}

高级用法

在HTTP服务中使用

结合Gone的gin组件,可以轻松实现HTTP请求的追踪:

func setupRouter(router gin.Router, tracer tracer.Tracer) {// 添加中间件,为每个请求设置追踪IDrouter.Use(func(c *gin.Context) {// 从请求头获取追踪ID,如果没有则生成新的traceId := c.GetHeader("X-Trace-ID")tracer.SetTraceId(traceId, func() {// 将追踪ID设置到响应头c.Header("X-Trace-ID", tracer.GetTraceId())c.Next()})})// 路由处理router.GET("/api/example", func(c *gin.Context) {// 在处理函数中可以直接获取追踪IDtraceId := tracer.GetTraceId()// 处理业务逻辑...})
}

微服务间传递追踪ID

// 客户端发送请求
func (c *Client) CallService() {c.tracer.SetTraceId("", func() {// 创建HTTP请求req, _ := http.NewRequest("GET", "http://service-b/api", nil)// 将追踪ID添加到请求头req.Header.Set("X-Trace-ID", c.tracer.GetTraceId())// 发送请求c.httpClient.Do(req)})
}// 服务端接收请求
func (s *Server) HandleRequest(w http.ResponseWriter, r *http.Request) {// 从请求头获取追踪IDtraceId := r.Header.Get("X-Trace-ID")s.tracer.SetTraceId(traceId, func() {// 处理请求// ...})
}

最佳实践

  1. 在服务入口点设置追踪ID:在HTTP处理器、gRPC服务方法等入口点使用SetTraceId设置追踪ID

  2. 使用tracer.Go代替标准的go关键字:确保子goroutine能够继承父goroutine的追踪ID

  3. 在日志中包含追踪ID:便于关联同一请求的不同日志条目

  4. 微服务调用中传递追踪ID:通过HTTP头或gRPC元数据传递追踪ID,实现跨服务的请求追踪

  5. 选择合适的实现方式:根据应用场景选择性能更优的实现方式

  6. 结合日志组件使用:将追踪ID自动添加到日志字段中,提高日志的可追踪性

注意事项

  1. 追踪ID不会自动跨越进程边界,需要手动在服务间请求中传递

  2. 微服务架构中,建议使用统一的头部字段(如X-Trace-ID)传递追踪ID

  3. 同一个goroutine中不要重复设置traceId,如果尝试重复设置,将会保留第一次设置的值

  4. 当traceId参数为空字符串时,会自动生成一个UUID作为traceId

  5. 在高并发场景下,tracerOverGid实现可能会有更好的性能表现


http://www.ppmy.cn/ops/166759.html

相关文章

React-state响应式内部数据(类组件Hook两种方式整理)

类组件 在类组件中要定义内部数据&#xff0c;由两种方案 构造器里面定义state属性&#xff0c;在这个对象中定义你需要的数据 constructor(){super()this.state {count:1}} 在类的下面直接定义一个属性state(推荐) state {count:10} 页面要使用state数据 <p>{t…

OSPF-5 3类LSA SummaryLSA

上一期我们介绍了2类LSA Network LSA的内容信息以及怎样从2类LSA中的信息描绘出一张具体的拓扑信息以及网段信息 这一期我们将介绍3类LSA Summary LSA区域间的LSA看看3类LSA是怎样把域间的路由信息传递到别的区域的 一、概述 由于3类LSA是用来描述我们域间的路由信息所以它是…

API自动化测试实战:Postman + Newman/Pytest的深度解析

引言 在数字化时代&#xff0c;API&#xff08;应用程序编程接口&#xff09;已成为系统间交互的核心纽带。无论是电商、金融、IoT还是云服务&#xff0c;API的稳定性、性能和安全性直接决定了产品的用户体验和业务连续性。然而&#xff0c;随着API复杂度的提升和迭代速度的加…

[GESP 202412 一级 T2] 奇数和偶数

描述 小杨有nn个正整数&#xff0c;他想知道其中的奇数有多少个&#xff0c;偶数有多少个。 输入描述 第一行包含一个正整数 nn&#xff0c;代表正整数个数。之后几行&#xff0c;每行包含一个正整数。 输出描述 输出两个正整数&#xff08;用英文空格间隔&#xff09;&am…

嵌入式硬件篇---龙芯GPIO控制

文章目录 前言1. 头文件引入作用 2. 导出GPIO引脚 export_gpio功能示例注意 3. 设置GPIO方向 set_gpio_direction功能示例 4. 设置GPIO值 set_gpio_value功能示例 5. 初始化函数 gpio_init功能 6.龙芯2K1000适配说明6.1 GPIO编号映射6.2 性能优化建议优点错误处理 6.3 权限问题…

TI的Doppler-Azimuth架构(TI文档)

TI在AWR2944平台上推出新的算法架构&#xff0c;原先的处理方式是做完二维FFT后在RD图上做CFAR检测&#xff0c;然后提取各个通道数据做测角。 Doppler-Azimuth架构则是做完二维FFT后&#xff0c;再做角度维FFT&#xff0c;生成Doppler-Azimuth频谱图&#xff0c;然后在该频谱图…

w259交通管理在线服务系统设计与实现

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…

0CTF 2016 piapiapia 1

#源码泄露 #代码审计 #反序列化字符逃逸 #strlen长度过滤数组绕过 www.zip 得到源码 看到这里有flag &#xff0c;猜测服务端docker的主机里&#xff0c;$flag变量应该存的就是我们要的flag。 于是&#xff0c;我们的目的就是读取config.php 利用思路 这里存在 任意文件读取…