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

Go从入门到精通(25) - 一个简单web项目-实现链路跟踪

Go从入门到精通(25)

一个简单web项目-实现链路跟踪


文章目录

  • Go从入门到精通(25)
  • 前言
  • 为什么需要分布式链路跟踪?
  • go实现链路跟踪
    • 搭建zipkin 服务
    • 安装依赖
    • 添加tracing包,OpenTelemetry 和Zipkin
    • 在 Gin 中集成 OpenTelemetry 中间件
    • log包添加获取traceId方法
    • 日志打印加入traceId
    • sql 操作加入链路跟踪
    • 客户端请求传递追踪上下文
    • 关键点总结


前言

分布式链路跟踪技术(Distributed Tracing)是分布式系统(尤其是微服务架构)中用于追踪请求全链路流转的核心技术。它通过记录请求在多个服务间的传播路径、执行耗时、状态等信息,帮助开发者定位跨服务调用的性能瓶颈、故障点和依赖关系,是保障分布式系统可观测性(Observability)的三大支柱之一(另外两个是日志和指标)。


为什么需要分布式链路跟踪?

在单体应用中,一个请求的处理流程在单一进程内完成,通过日志即可定位问题;但在分布式系统(如微服务)中,一个用户请求可能经历以下流程:

客户端 → API网关 → 认证服务 → 订单服务 → 库存服务 → 数据库 → 缓存

这种场景下,传统调试方式面临三大挑战:

  • 链路断裂:无法确定请求经过了哪些服务、调用顺序如何;
  • 责任模糊:某个请求超时 / 失败时,无法判断是哪个服务导致(如订单服务本身慢,还是依赖的库存服务响应延迟);
  • 性能盲区:无法量化各服务的耗时占比(如 90% 的耗时在数据库,还是在网络传输)。
    分布式链路跟踪技术通过全链路数据采集与可视化,解决了这些问题。

go实现链路跟踪

这里以 OpenTelemetry+Zipkin为例实现链路跟踪

搭建zipkin 服务

一般公司会统一搭建zipkin服务,自行搭建参考官网

安装依赖

go get “go.opentelemetry.io/contrib/propagators/b3”
go get “go.opentelemetry.io/otel”
go get “go.opentelemetry.io/otel/exporters/zipkin”
go get “go.opentelemetry.io/otel/propagation”
go get “go.opentelemetry.io/otel/sdk/resource”
go get “go.opentelemetry.io/otel/sdk/trace”
go get go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin

添加tracing包,OpenTelemetry 和Zipkin

//tracing/tracing.go
package tracingimport ("go-web-demo/logger""go.opentelemetry.io/contrib/propagators/b3""go.opentelemetry.io/otel""go.opentelemetry.io/otel/exporters/zipkin""go.opentelemetry.io/otel/propagation""go.opentelemetry.io/otel/sdk/resource""go.opentelemetry.io/otel/sdk/trace"semconv "go.opentelemetry.io/otel/semconv/v1.30.0"oteltrace "go.opentelemetry.io/otel/trace""go.uber.org/zap""os"
)var (tracer oteltrace.Tracer
)func InitTracer() error {// 初始化链路跟踪zipkinEndpoint := os.Getenv("ZIPKIN_ENDPOINT")if zipkinEndpoint == "" {logger.Sugar.Warn("ZIPKIN_ENDPOINT 未设置,链路跟踪将被禁用")return nil}zipkinExporter, err := zipkin.New(zipkinEndpoint)if err != nil {zap.L().Error("Failed to create Zipkin exporter", zap.Error(err))return err}tp := trace.NewTracerProvider(trace.WithSampler(trace.AlwaysSample()),trace.WithBatcher(zipkinExporter),trace.WithResource(resource.NewWithAttributes(semconv.SchemaURL, semconv.ServiceName("go-web-demo"))),)otel.SetTracerProvider(tp)// 创建专门的W3C propagatorw3cPropagator := propagation.TraceContext{}// 创建B3 propagator,同时支持单头和多头格式b3Propagator := b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader | b3.B3SingleHeader),)otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(w3cPropagator, b3Propagator, propagation.TraceContext{}, propagation.Baggage{}))tracer = otel.Tracer("go-web-demo")logger.Sugar.Infow("Zipkin 链路跟踪初始化成功,服务名: %s,端点: %s", "go-web-demo", zipkinEndpoint)return nil
}
  • zipkinEndpoint 来源于你搭建的服务地址
  • trace.WithSampler(trace.AlwaysSample()) 这里使用100%采样,如果生成环境可以做适当的调整,比如0.1
sampler := sdktrace.TraceIDRatioBased(0.1) // 采样率10%

在 Gin 中集成 OpenTelemetry 中间件

import "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"// 创建默认引擎,包含日志和恢复中间件router := gin.Default()// 添加Zipkin中间件router.Use(otelgin.Middleware("go-web-demo")) //其他之前的逻辑。。

log包添加获取traceId方法

func WithTraceCtx(ctx context.Context) *zap.Logger {span := trace.SpanFromContext(ctx)if !span.SpanContext().IsValid() {return Logger}traceID := span.SpanContext().TraceID().String()spanID := span.SpanContext().SpanID().String()return Logger.With(zap.String("traceId", traceID),zap.String("spanId", spanID),)
}

日志打印加入traceId

func HealthHandler(c *gin.Context) {ctx := c.Request.Context()logger.WithTraceCtx(ctx).Info("health check")c.JSON(http.StatusOK, gin.H{"message": "success"})
}

日志打印效果

{"service": "user-api", "traceId": "579ec5e6c0a21967e628266103499008", "spanId": "9053af570d799499"}

当然我们也可以在zipkin服务器查看,把我们日志的traceId输入并且搜索就可以得到类型下图的效果。大家也可以多搭建一个服务调用效果更佳
在这里插入图片描述

sql 操作加入链路跟踪

//repository/database_connection.go
package repositoryimport	gormTracing "gorm.io/plugin/opentelemetry/tracing"
func InitGormDB() error {//。。。 之前的的数据库连接逻辑//添加链路跟踪err = DB.Use(gormTracing.NewPlugin(//这里使用实际的数据类型,比如mysqlgormTracing.WithDBSystem(dbDriver),gormTracing.WithoutServerAddress(),))
}

客户端请求传递追踪上下文

// 调用其他服务并传递追踪上下文
func callAnotherService(ctx context.Context, url string) (*http.Response, error) {// 从上下文中获取当前spanspan := trace.SpanFromContext(ctx)// 创建HTTP请求req, err := http.NewRequestWithContext(ctx, "GET", url, nil)if err != nil {span.RecordError(err)span.SetStatus(codes.Error, err.Error())return nil, err}// 使用OpenTelemetry传播器将上下文注入到请求头otel.GetTextMapPropagator().Inject(ctx,propagation.HeaderCarrier(req.Header),)// 发送请求client := &http.Client{}resp, err := client.Do(req)if err != nil {span.RecordError(err)span.SetStatus(codes.Error, err.Error())return nil, err}// 记录响应状态span.SetAttributes(attribute.Int("http.status_code", resp.StatusCode))if resp.StatusCode >= 400 {span.SetStatus(codes.Error, resp.Status)}return resp, nil
}

关键点总结

  • 使用 OpenTelemetry 标准:
    通过 go.opentelemetry.io/otel 实现与 Zipkin 的集成,遵循 CNCF 标准。
  • 中间件集成:
    使用 go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin 自动处理 Gin 请求的追踪。
http://www.xdnf.cn/news/15466.html

相关文章:

  • audiorecord 之 抢占优先级
  • 数据库询问RAG框架Vanna的总体架构
  • CMake基础:覆盖项目开发的五大配套工具
  • 数据结构——顺序表的相关操作
  • 信息学奥赛一本通 1552:【例 1】点的距离
  • 【Keil】C/C++混合编程的简单方法
  • 内存的基础相关知识,什么是内存,内存管理
  • 学习C++、QT---26(QT中实现记事本项目实现文件路径的提示、C++类模板、记事本的行高亮的操作的讲解)
  • LVS(Linux Virtual Server)详细笔记(理论篇)
  • 202507中央城市工作会议
  • 【Java】JUC并发(线程的方法、多线程的同步并发)
  • UE5多人MOBA+GAS 23、制作一个地面轰炸的技能
  • SHAP 值的数值尺度
  • 梳理Bean的创建流程
  • burpsuite使用中遇到的一些问题(bp启动后浏览器无法连接)/如何导入证书
  • GPIO 输入/输出
  • 2025年睿抗机器人开发者大赛CAIP-编程技能赛-高职组(省赛)解题报告 | 珂学家
  • 在Autodl服务器中使用VNC建立图形界面
  • 快速排序:原理、示例与 C 语言实现详解
  • C语言---自定义类型(下)(枚举和联合类型)
  • 云服务器如何管理数据库(MySQL/MongoDB)?
  • 【html常见页面布局】
  • 50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | DoubleClickHeart(双击爱心)
  • netstat -tlnp | grep 5000
  • Swift实现股票图:从基础到高级
  • python的形成性考核管理系统
  • 并发-原子变量类
  • 【MCU控制 初级手札】1.1 电阻
  • 现代CSS实战:用变量与嵌套重构可维护的前端样式
  • 使用 Java 获取 PDF 页面信息(页数、尺寸、旋转角度、方向、标签与边框)