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.pswp.cn/pingmian/89618.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/89618.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/89618.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年最新秋招java后端面试八股文+场景题

一、Java核心八股文(2025年最新版)1. Java基础HashMap vs ConcurrentHashMapHashMap:非线程安全,JDK1.8后采用数组链表/红黑树,扩容时可能死循环(JDK1.7)。ConcurrentHashMap:JDK1.8…

esp32 sd卡

ref: platform io & arduino Boards — PlatformIO latest documentation https://github.com/espressif/arduino-esp32/blob/master/libraries/SD_MMC/README.md SD 卡实验 | 极客侠GeeksMan GitHub - fabianoriccardi/ESPLogger: An Arduino library pro…

Java学习--------消息队列的重复消费、消失与顺序性的深度解析​

在 Java 分布式系统开发中,消息队列的应用已十分普遍。但随着业务规模扩大,消息的重复消费、意外消失、顺序错乱等问题逐渐成为系统稳定性的隐患。本文将从 Java 开发者的视角,深入分析这三大问题的产生原因、业务后果,并结合具体…

【Oracle】centos7离线静默安装oracle11g(p13390677_112040)

博文地址:https://blog.csdn.net/gitblog_06670/article/details/142569814 仓库地址:https://gitcode.com/Open-source-documentation-tutorial/31eb1/?utm_sourcedocument_gitcode&indexbottom&typecard 参考安装地址: 收费版&…

智能设备畅想

### 智能设备畅想 突然想到了一个好主意 因为最近在查无人机的相关资料(很早之前就想搞个无人机玩玩但始终没有买) 在了解自组装方面的内容时,和AI沟通了下 正好之前组装的 小智AI 基本上已经完善了,也正在考虑其在其他方向拓展的…

SpringAI——ChatModel

我的前面一篇文章(SpringAI——ChatClient配置与使用)中讲了ChatClient,它是一个构建于 ChatModel 之上的高层封装,它提供了更丰富的对话交互能力。可以这么说ChatModel相当于发动机,ChatClient相当于一台含有发动机、…

Zabbix监控K8S的PV信息详细教程!

文将介绍如何使用Zabbix自定义键值脚本方式监控K8S的PV卷状态等信息。 在Kubernetes (K8S) 中,PersistentVolume (PV) 是集群中的一个抽象层,它代表了底层存储资源,例如网络存储系统(如NFS、Ceph、GlusterFS等)或本地存…

wx小程序原生开发使用高德地图api

第一步:注册高德地图api的key第二步:下载amap-wx.js 放到项目的某个目录第三步:配置app.json(必须,因为需要定位功能,)"requiredPrivateInfos": ["getLocation"],"per…

如何通过mac的前24bit,模糊确认是那一台什么样的设备

MAC Address Lookup - MAC/OUI/IAB/IEEE Vendor Manufacturer Search Wireshark • Go Deep 上面这两个网址提供了,正对mac 的前24位,查找对应的网络设备厂商信息,可以让我们在运维过程中模糊的判断大约是什么型号的设备 使用macvendorloo…

【爬虫】04 - 高级数据存储

爬虫04 - 高级数据存储 文章目录爬虫04 - 高级数据存储一:加密数据的存储二:JSON Schema校验三:云原生NoSQL(了解)四:Redis Edge近端计算(了解)五:二进制存储1:Pickle2:Parquet一:加…

UDP和TCP的主要区别是什么?

在网络通信中,TCP(传输控制协议)和UDP(用户数据报协议)是两种核心的传输层协议。它们各自的特点和应用场景截然不同,理解两者的区别对于选择合适的通信方式至关重要。本文将通过几个关键点,用简…

Softhub软件下载站实战开发(十八):软件分类展示

Softhub软件下载站实战开发(十八):软件分类展示 🖥️ 在之前文章中,我们实现了后台管理相关部分,本篇文章开始我们来实现用户端页面,由于内网使用,不需要sso优化等特性,我…

linux--------------------BlockQueue的生产者消费模型

1.基础BlockingQueue的生产者消费模型 1.1 BlockQueue 在多线程编程中阻塞队列是一种常用于实现生产者和消费者模型的数据结构,它与普通的队列区别在于,当队列为空时,从队列获取元素的操作将被阻塞,直到队列中放入了新的数据。当…

堆排序算法详解:原理、实现与C语言代码

堆排序(Heap Sort)是一种高效的排序算法,利用二叉堆数据结构实现。其核心思想是将待排序序列构造成一个大顶堆(或小顶堆),通过反复调整堆结构完成排序。下面从原理到实现进行详细解析。一、核心概念&#x…

SSM框架——注入类型

引用类型的注入:Setter方法简单类型的注入:定义简单实例和方法在配置文件中对bean进行配置,使用porperty属性 值用value(ref是用来获取bean的)构造器方法:构造器方法中需要写name,这样程序就会耦…

信息学奥赛一本通 1552:【例 1】点的距离

【题目链接】 ybt 1552:【例 1】点的距离 【题目考点】 1. 最近公共祖先(LCA):倍增求LCA 知识点讲解见:洛谷 P3379 【模板】最近公共祖先(LCA) 【解题思路】 首先用邻接表保存输入的无权图…

1Panel中的OpenResty使用alias

问题 在服务器上使用了1Panel的OpenResty来管理网站服务,当作是一个Nginx用,想做一个alias来直接管理某个文件夹的文件,于是直接在其中一个网站中使用了alias配置。 location /upload {alias /root/upload;autoindex on;charset utf-8;charse…

小明记账簿焕新记:从单色到多彩的主题进化之路

【从冷静蓝到多彩世界,这一次我们重新定义记账美学】 曾经,打开“小明记账簿”是一片沉稳的蓝色海洋,它像一位理性的财务管家,默默守护着你的每一笔收支。但总有人悄悄问:“能不能多一些颜色?”今天&#x…

Apache IoTDB(1):时序数据库介绍与单机版安装部署指南

目录一、Apache IoTDB 是什么?1.1 产品介绍1.2 产品体系1.3 产品架构二、IoTDB 环境配置2.1 Linux系统需准备环境2.2 Windows系统需准备环境2.3 网络配置2.3.1 关闭防火墙2.3.2 查看端口是否占用2.3.3 避雷经验三、IoTDB 单机版系统部署安装指南3.1 产品下载3.2 注意…

Python 图片爬取入门:从手动下载到自动批量获取

前言 想批量下载网页图片却嫌手动保存太麻烦?本文用 Python 带你实现自动爬取,从分析网站到代码运行,步骤清晰,新手也能快速上手,轻松搞定图片批量获取。 1.安装模块 在开始爬取图片前,我们需要准备好工具…