golang 链路追踪的实现示例

 更新时间:2025年03月03日 10:39:09   作者:爱编程的 小李  
本文主要介绍了golang 链路追踪的实现示例,包括调用链过长和接口响应慢的问题,具有一定的参考价值,感兴趣的可以了解一下

分布式链路追踪(Distributed Tracing),也叫分布式链路跟踪,分布式跟踪,分布式追踪等等。

问题

场景1:调用链过长,查询很慢

在这里插入图片描述

web-gin由A负责,服务A由B负责,某个接口出现异常了,先查询日志看是哪个地方出错了,发现服务A调用失败,依次找到D

场景2:接口相应很慢

在这里插入图片描述

解决方法

(1)打日志:慢(可能不看懂其他人写的日志)
(2)ELK:可以解决,也是日志需要写入
(3)第三方链路追踪系统可以解决

链路追踪系统技术选型

java使用zipkin和skywalking比较多,而go使用jaeger多

zipkinjaegerskywalking
OpenTracing
客户端支持语言java、c#、php、python等java、c#、go、php、python等java、.Net Core、NodeJS、PHP、python
存储ES、Mysql、Cassandra内存ES、kafka、Cassandra内存ES、H2、mysql、TIDB、shard sphere
传输协议支持http、MQudp/httpgRPC
ui丰富程度
实现方式-代码侵入式拦截请求,侵入拦截请求,侵入字节码注入,无侵入
扩展性
trace查询支持支持支持
性能损失
选择jaeger:官方支持jaeger
技术选型原则:
客户端支持、综合考虑、什么语言开发的

安装jaeger

docker run \
  --rm \
  --name jaeger \
  -p6831:6831/udp \
  -p16686:16686 \
  jaegertracing/all-in-one:latest

架构

在这里插入图片描述

Jaeger组成

1.Jaeger Client - 为不同语言实现了符合 OpenTracing 标准的 SDK。应用程序通过 API 写入数据,client library 把 trace 信息按照应用程序指定的采样策略传递给 jaeger-agent。
2.Agent - 它是一个监听在 UDP 端口上接收 span 数据的网络守护进程,它会将数据批量发送给 collector。它被设计成一个基础组件,部署到所有的宿主机上。Agent 将 client library 和 collector 解耦,为 client library 屏蔽了路由和发现 collector 的细节。2.Collector - 接收 jaeger-agent 发送来的数据,然后将数据写入后端存储。Collector 被设计成无状态的组件,因此您可以同时运行任意数量的 jaeger-collector。
3.Data Store - 后端存储被设计成一个可插拔的组件,支持将数据写入 cassandra、elastic search。
4.Query - 接收查询请求,然后从后端存储系统中检索 trace 并通过 UI 进行展示。 Query 是无状态的,您可以启动多个实例,把它们部署在 nginx 这样的负载均衡器后面。
分布式追踪系统发展很快,种类繁多,但核心步骤一般有三个:代码埋点,数据存储、查询展示订单调用过程

在这里插入图片描述

添加jaeger

sudo go get github.com/jaegertracing/jaeger-client-go

发送span

package main

import (
	"time"

	"github.com/uber/jaeger-client-go"
	jaegercfg "github.com/uber/jaeger-client-go/config"
)

func main() {
	cfg := jaegercfg.Configuration{
		Sampler: &jaegercfg.SamplerConfig{
			Type:  jaeger.SamplerTypeConst,
			Param: 1, //[0,1] 0不采样1一直采样
		},
		Reporter: &jaegercfg.ReporterConfig{
			LogSpans:           true, //打不打印日志
			LocalAgentHostPort: "192.168.31.19:6831",
		},
		ServiceName: "shop",
	}
	tracer, closer, err := cfg.NewTracer(jaegercfg.Logger(jaeger.StdLogger))
	if err != nil {
		panic(err)
	}
	defer closer.Close()
	span := tracer.StartSpan("go-grpc-web")
	time.Sleep(time.Second)
	defer span.Finish()
}

嵌套span

package main

import (
	"time"

	"github.com/opentracing/opentracing-go"
	"github.com/uber/jaeger-client-go"
	jaegercfg "github.com/uber/jaeger-client-go/config"
)

func main() {
	cfg := jaegercfg.Configuration{
		Sampler: &jaegercfg.SamplerConfig{
			Type:  jaeger.SamplerTypeConst,
			Param: 1, //[0,1] 0不采样1一直采样
		},
		Reporter: &jaegercfg.ReporterConfig{
			LogSpans:           true, //打不打印日志
			LocalAgentHostPort: "39.103.59.35:6831",
		},
		ServiceName: "shop",
	}
	tracer, closer, err := cfg.NewTracer(jaegercfg.Logger(jaeger.StdLogger))
	if err != nil {
		panic(err)
	}
	defer closer.Close()
	parentSpan := tracer.StartSpan("main")

	spanA := tracer.StartSpan("funcA", opentracing.ChildOf(parentSpan.Context()))
	time.Sleep(time.Millisecond * 500)
	spanA.Finish()

	spanB := tracer.StartSpan("funcB", opentracing.ChildOf(parentSpan.Context()))
	time.Sleep(time.Millisecond * 1000)
	spanB.Finish()

	parentSpan.Finish()
}

grpc使用jaeger

package main

import (
	"context"
	"fmt"
	"japter_test/proto"

	"japter_test/otgrpc"

	"github.com/opentracing/opentracing-go"
	"github.com/uber/jaeger-client-go"
	jaegercfg "github.com/uber/jaeger-client-go/config"
	"google.golang.org/grpc"
)

func main() {
	cfg := jaegercfg.Configuration{
		ServiceName: "mxshop",
		Sampler: &jaegercfg.SamplerConfig{
			Type:  jaeger.SamplerTypeConst,
			Param: 1, //[0,1] 0不采样1一直采样
		},
		Reporter: &jaegercfg.ReporterConfig{
			LogSpans:           true, //打不打印日志
			LocalAgentHostPort: "39.103.59.35:6831",
		},
	}
	tracer, closer, err := cfg.NewTracer(jaegercfg.Logger(jaeger.StdLogger))
	if err != nil {
		panic(err)
	}
	opentracing.SetGlobalTracer(tracer)
	defer closer.Close()

	conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure(), grpc.WithUnaryInterceptor(otgrpc.OpenTracingClientInterceptor(opentracing.GlobalTracer())))
	if err != nil {
		panic(err)
	}
	defer conn.Close()
	c := proto.NewGreeterClient(conn)
	r, err := c.SayHello(context.Background(), &proto.HelloRquest{
		Name: "bobby",
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(r.Message)
}

gin使用jaeger

拦截器

package middlewares

import (
	"GolangStudy/Practice/shop/goods-web/global"
	"fmt"

	"github.com/gin-gonic/gin"
	"github.com/opentracing/opentracing-go"
	"github.com/uber/jaeger-client-go"
	jaegerConfig "github.com/uber/jaeger-client-go/config"
)

func Trace() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		cfg := jaegerConfig.Configuration{
			ServiceName: "your_service_name",
			Sampler: &jaegerConfig.SamplerConfig{
				Type:  jaeger.SamplerTypeConst,
				Param: 1, // 1 = always sample
			},
			Reporter: &jaegerConfig.ReporterConfig{
				LogSpans:           true,
				LocalAgentHostPort: fmt.Sprintf("%s:%d", global.ServerConfig.JaegerInfo.Host, global.ServerConfig.JaegerInfo.Port), // Jaeger Query 服务的地址和端口
			},
		}
		tracer, closer, err := cfg.NewTracer(jaegerConfig.Logger(jaeger.StdLogger))
		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()
	}
}

使用

GoodsRouter := Router.Group("goods").Use(middlewares.Trace())

到此这篇关于golang 链路追踪的实现示例的文章就介绍到这了,更多相关golang 链路追踪内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • 一文带你了解Go语言中的类型断言和类型转换

    一文带你了解Go语言中的类型断言和类型转换

    在Go中,类型断言和类型转换是一个令人困惑的事情,他们似乎都在做同样的事情。最明显的不同点是他们具有不同的语法(variable.(type) vs type(variable) )。本文我们就来深入研究一下二者的区别
    2022-09-09
  • golang gc的内部优化详细介绍

    golang gc的内部优化详细介绍

    Go编译器在垃圾回收(GC)的扫描标记阶段,对存储无指针键值对的map进行了优化,即在GC扫描时不深入扫描map内部数据,只检查map本身是否需要回收,这一优化显著提升了GC扫描的速度,从而减少了GC对程序性能的影响
    2024-10-10
  • golang使用map支持高并发的方法(1000万次操作14ms)

    golang使用map支持高并发的方法(1000万次操作14ms)

    这篇文章主要介绍了golang使用map支持高并发的方法(1000万次操作14ms),本文给大家详细讲解,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-11-11
  • Go中strings包的基本使用示例代码

    Go中strings包的基本使用示例代码

    本文详细介绍了Go语言中strings包的基本使用方法,包括字符串的前缀、后缀判断,字符串包含、索引查找、字符串替换、计数、重复、大小写转换、修剪、分割、拼接以及数据类型转换等功能,示例代码丰富,适合初学者和需要使用字符串处理功能的开发者参考学习
    2024-10-10
  • golang coroutine 的等待与死锁用法

    golang coroutine 的等待与死锁用法

    这篇文章主要介绍了golang coroutine 的等待与死锁用法详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05
  • GoFrame框架garray并发安全数组使用开箱体验

    GoFrame框架garray并发安全数组使用开箱体验

    这篇文章主要介绍了GoFrame框架garray并发安全数组使用开箱体验,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • 为什么GO不支持循环引用

    为什么GO不支持循环引用

    这篇文章主要介绍的是为什么GO不支持循环引用,学习 Go 语言的开发者越来越多了,很多小伙伴在使用时,就会遇到种种不理解的问题,其中一点就是包的循环引用的报错,下main文章我们一起来看看学习原因
    2021-10-10
  • Go处理PDF的实现代码

    Go处理PDF的实现代码

    这篇文章主要介绍了Go处理PDF的实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • Go语言实现汉诺塔算法

    Go语言实现汉诺塔算法

    之前的文章,我们给大家分享了不少汉诺塔算法的实现语言,包括C、c++、java、python等,今天我们就来使用go语言来实现一下,需要的小伙伴来参考下吧。
    2015-03-03
  • Go语言的反射机制进阶实现

    Go语言的反射机制进阶实现

    反射是Go语言的一个强大特性,它允许程序在运行时检查和操作变量、接口和结构体,本文就来详细的介绍一下Go语言的反射机制的实现,感兴趣的可以了解一下
    2026-04-04

最新评论