使用 OpenTelemetry 和 Golang

入门

img

在本文中,我将展示你需要配置和处理统计信息所需的基本代码。在这个简短的教程中,我们将使用 Opentelemetry 来集成我们的 Golang 代码,并且为了可视化,我们将使用 Jeager。

在开始之前,让我简要介绍一下什么是 Opentelemetry。

什么是 Opentelemetry?

OpenTelemetry 是一个开源项目,旨在促进微服务和分布式应用的可观察性和可追踪性。它提供了一种标准化的方式来实现、生成记录并收集我们服务的指标,并且可以在不同的编程语言中实现。

其主要目标之一是解决以一致可靠的方式从分布式系统中收集遥测数据的挑战。Opentelemetry 是如何实现的呢?这项技术提供了库和工具,允许开发人员自动向他们的应用程序和组件添加工具,从而轻松生成和收集遥测数据,比如响应时间、延迟、错误、执行跟踪和性能指标等信息。

注意:
你可以在以下官方链接中找到更多有关 Opentelemetry 的信息:
https://opentelemetry.io/docs/what-is-opentelemetry/

img

我之前提到的另一个技术是 Jaeger。虽然它不是本文的中心话题,但我们将使用它来可视化收集的遥测数据。

Jaeger 是一个用于监视和调试分布式系统的开源跟踪平台。它为请求在分布式环境中传播时提供跟踪和追踪功能。

它的主要功能是捕获、存储和显示关于分布式系统中请求流动的详细信息。这包括有关运行时、依赖关系以及单个组件之间的交互的信息。

注意:
你可以在官方网站中找到关于 Jaeger 的更多信息:https://www.jaegertracing.io/docs/1.45/

实施时间!

现在你已经了解了 OpenTelemetry 和 Jeager,是时候构建两个简单的应用程序了。

  • 第一个将向我们展示如何在我们的 Golang 应用程序中配置 OpenTelemetry,并通过运行一些函数来学习。在这里,我们将了解跟踪的工作原理,以及如何在 Jeager 上可视化它们。
  • 第二个是基于 gin-gonic 框架的服务实现。

我们开始吧!

首先,我们需要运行一个 Jeager 实例,在这种情况下,我们将使用 Docker 使其变得简单。

Jaeger 提供了一个我们可以用于教程的 all-in-one 发行版。

docker run -d --name jaeger \-e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \-e COLLECTOR_OTLP_ENABLED=true \-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.45

注意:对于生产环境,你应该根据需要进行适当的配置。

运行并构建容器后,你可以通过以下链接访问 Jaeger:http://127.0.0.1:16686/search。

你将看到如下页面:

img

持续运行并让我们开始编码吧。

集成 Opentelemetry

让我们从一个简单的示例开始:

我们需要以下依赖:

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/jaeger"

你需要配置应用程序以将遥测数据发送到 Jaeger。此函数使用默认的 Jaeger URL:PORT 创建导出器。

我将尽量添加良好的注释,以指导你理解代码。

首先,定义下面几个常量,我们将在配置 OpenTelemetry 和导出器的函数中使用它们。

  • Service:帮助我们识别生成 span 的服务。
  • Environment:帮助我们按部署(例如生产、测试、开发)对服务 span 进行分组。
  • ID:帮助我们标识 span 组。
const (service     = "medium-tutorial"environment = "development"id          = 1
)

然后是函数:

func tracerProvider() (*tracesdk.TracerProvider, error) {// 创建 Jaeger 导出器exp, err := jaeger.New(jaeger.WithCollectorEndpoint())if err != nil {return nil, err}tp := tracesdk.NewTracerProvider(// 在生产环境中始终确保进行批处理。tracesdk.WithBatcher(exp),// 记录有关此应用程序的信息到资源中。tracesdk.WithResource(resource.NewWithAttributes(semconv.SchemaURL,semconv.ServiceName(service),attribute.String("environment", environment),attribute.Int64("ID", id),)),)return tp, nil
}

正如你所见,在此我们创建了导出器,同时我们使用了 Jeager(你可以在官方网站上查看完整的导出器列表)。之后,我们开始配置跟踪器提供程序,在那里我们定义了诸如 exporterserviceName 等参数,这些参数有助于在 Jaeger 上识别指标,并且像 environment 这样的参数有助于我们识别部署情况。

对于 main 方法,我们需要定义类似下面这样的代码示例:

tp, err := tracerProvider()if err != nil {log.Fatal(err)}// Register our TracerProvider as the global so any imported// instrumentation in the future will default to using it.otel.SetTracerProvider(tp)ctx, cancel := context.WithCancel(context.Background())defer cancel()// Cleanly shutdown and flush telemetry when the application exits.defer func(ctx context.Context) {// Do not make the application hang when it is shutdown.ctx, cancel = context.WithTimeout(ctx, time.Second*5)defer cancel()if err := tp.Shutdown(ctx); err != nil {log.Fatal(err)}}(ctx)tr := tp.Tracer("component-main")ctx, span := tr.Start(ctx, "foo")defer span.End()

代码并不复杂,我们只是利用创建连接的函数,然后需要创建一个上下文。

我们的指标从这里开始:

tr := tp.Tracer("component-main")
ctx, span := tr.Start(ctx, "foo")
defer span.End()

这些代码行将帮助我们启动 span,跟踪器是主要组件,span 名称为 foo,并且你可以看到我们捕获了上下文,因为我们将使用该上下文来分组 span。(别担心,我会向你展示在 Jaeger 中的结果)

最后,这是完整的代码:

package mainimport ("context""fmt""log""time""go.opentelemetry.io/otel""go.opentelemetry.io/otel/attribute""go.opentelemetry.io/otel/exporters/jaeger""go.opentelemetry.io/otel/sdk/resource"tracesdk "go.opentelemetry.io/otel/sdk/trace"semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
)const (service     = "medium-tutorial"environment = "development"id          = 1
)// tracerProvider returns an OpenTelemetry TracerProvider configured to use
// the Jaeger exporter that will send spans to the provided url. The returned
// TracerProvider will also use a Resource configured with all the information
// about the application.
func tracerProvider() (*tracesdk.TracerProvider, error) {// Create the Jaeger exporterexp, err := jaeger.New(jaeger.WithCollectorEndpoint())if err != nil {return nil, err}tp := tracesdk.NewTracerProvider(// Always be sure to batch in production.tracesdk.WithBatcher(exp),// Record information about this application in a Resource.tracesdk.WithResource(resource.NewWithAttributes(semconv.SchemaURL,semconv.ServiceName(service),attribute.String("environment", environment),attribute.Int64("ID", id),)),)return tp, nil
}func main() {tp, err := tracerProvider()if err != nil {log.Fatal(err)}// Register our TracerProvider as the global so any imported// instrumentation in the future will default to using it.otel.SetTracerProvider(tp)ctx, cancel := context.WithCancel(context.Background())defer cancel()// Cleanly shutdown and flush telemetry when the application exits.defer func(ctx context.Context) {// Do not make the application hang when it is shutdown.ctx, cancel = context.WithTimeout(ctx, time.Second*5)defer cancel()if err := tp.Shutdown(ctx); err != nil {log.Fatal(err)}}(ctx)tr := tp.Tracer("component-main")ctx, span := tr.Start(ctx, "foo")defer span.End()bar(ctx)time.Sleep(10 * time.Second)
}func bar(ctx context.Context) {fmt.Println("on bar")tr := otel.Tracer("component-bar")_, span := tr.Start(ctx, "bar")span.SetAttributes(attribute.Key("medium_test").String("this is an attribute value"))defer span.End()time.Sleep(200 * time.Millisecond)}

如果我们运行程序,然后转到 Jaeger 的主页面,可以通过选择以下参数来搜索我们的统计信息:

服务:medium-tutorial
操作:所有

img

在上面的图像中,您可以看到当前的跨度。如果单击其中一个,将会看到更多信息:

img

img

在那里,您可以看到上面的注释行,您可以可视化跨度及其子跨度,这非常有帮助,因为可以追踪您的逻辑传播和流程。您可以在那里可视化的其他重要指标是每个过程(span)完成所花费的时间。

这里我想留下一条注释:如果您想了解更多有关跨度属性的信息,建议您查阅官方文档,本文只是一个简单的教程。

很简单,对吧?现在我们知道如何发送跨度以及如何对它们进行分组,我们可以将其实现到我们的 gin-gonic 实现中。让我们看一下它是什么样子的。

在 gin-gonic 服务中实现 OpenTelemetry

在以下代码中,您将找到一个简单的 gin-gonic 的 open telemetry 实现,我们可以重用用于配置导出器的基本配置。

此外,在此代码中,您将了解如何处理 Go 协程跨度。

以下是完整的代码:

package mainimport ("context""fmt""log""net/http""time""github.com/gin-gonic/gin""go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin""go.opentelemetry.io/otel""go.opentelemetry.io/otel/attribute""go.opentelemetry.io/otel/exporters/jaeger"tracesdk "go.opentelemetry.io/otel/sdk/trace""go.opentelemetry.io/otel/propagation""go.opentelemetry.io/otel/sdk/resource"semconv "go.opentelemetry.io/otel/semconv/v1.17.0"oteltrace "go.opentelemetry.io/otel/trace"
)const (service     = "medium-gin-server-test"environment = "development"id          = 1
)var tracer = otel.Tracer(service)func tracerProvider() (*tracesdk.TracerProvider, error) {// Create the Jaeger exporterexp, err := jaeger.New(jaeger.WithCollectorEndpoint())if err != nil {return nil, err}tp := tracesdk.NewTracerProvider(// Always be sure to batch in production.tracesdk.WithSampler(tracesdk.AlwaysSample()),tracesdk.WithBatcher(exp),tracesdk.WithResource(resource.NewWithAttributes(semconv.SchemaURL,semconv.ServiceName(service),attribute.String("environment", environment),attribute.Int64("ID", id),),),)// Register our TracerProvider as the global so any imported// instrumentation in the future will default to using it.otel.SetTracerProvider(tp)otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))return tp, nil
}func main() {fmt.Println("initializing")tp, err := tracerProvider()if err != nil {log.Fatal(err)}// Cleanly shutdown and flush telemetry when the application exits.defer func() {if err := tp.Shutdown(context.Background()); err != nil {log.Fatal(err)}}()r := gin.New()r.Use(otelgin.Middleware("my-server"))loadRoutes(r)r.Run()
}func loadRoutes(r *gin.Engine) {r.GET("/ping", pingFunc)
}func pingFunc(c *gin.Context) {ctx, span := tracer.Start(c.Request.Context(), "/ping", oteltrace.WithAttributes(attribute.String("hello", "the user")))defer span.End()bar(ctx)c.JSON(http.StatusOK, gin.H{"message": "pong",})
}func ping2Func(c *gin.Context) {ctx, span := tracer.Start(c.Request.Context(), "/ping-2", oteltrace.WithAttributes(attribute.String("hello2", "the user 2")))defer span.End()bar(ctx)c.JSON(http.StatusOK, gin.H{"message": "pong",})
}func bar(ctx context.Context) {fmt.Println("on bar")// Use the global TracerProvider.ct, span := tracer.Start(ctx, "bar")span.SetAttributes(attribute.Key("testset").String("value"))defer span.End()time.Sleep(1 * time.Millisecond)go bar3(ct)
}func bar3(ctx context.Context) {fmt.Println("on bar 3")_, span := tracer.Start(ctx, "bar-3-on-goroutine")span.AddEvent("starting goroutine bar3")defer func() {span.End()}()span.AddEvent("executing logic")time.Sleep(1 * time.Second)span.AddEvent("completed goroutine bar3")
}

运行代码并向 ping 端点发出请求后,您可以转到 Jaeger 并搜索跨度。

img

这将显示类似于以下的内容:

img

然后,如果单击结果,您将能够查看所有跨度的追踪:

img

img

在此示例中,我们可以将跨度视为级联,因为当我们通过函数调用传递上下文时,我们生成了跨度层次结构。

现在您可以使用代码并进行修改,以便更深入了解跨度追踪。

正如您所见,使用可视化跟踪和跨度的工具将 OpenTelemetry 集成到我们的项目中并不复杂。

您可以使用我们示例中定义的代码开始配置您的真实服务,只需根据需要进行调整即可。正如您所见,这并不复杂,易于理解。

感谢阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/186401.shtml

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

相关文章

go学习之json和单元测试知识

文章目录 一、json以及序列化1.概述2.json应用场景图3.json数据格式说明4.json的序列化1)介绍2)应用案例 5.json的反序列化1)介绍2)应用案例 二、单元测试1.引子2.单元测试-基本介绍3.代码实现4.单元测试的细节说明5.单元测试的综…

中国毫米波雷达产业分析4——毫米波雷达企业介绍

一、矽典微 (一)公司简介 矽典微致力于实现射频技术的智能化,专注于研发高性能无线技术相关芯片,产品广泛适用于毫米波传感器、下一代移动通信、卫星通信等无线领域。 整合自身在芯片、系统、软件、算法等领域的专业能力&#xf…

【论文速递】:老驾驶员轨迹数据中的异常行为检测

给定道路网络和一组轨迹数据,异常行为检测 (ABD) 问题是识别在行程中表现出明显方向偏差、急刹车和加速的驾驶员。ABD 问题在许多社会应用中都很重要,包括轻度认知障碍 (MCI) 检测和老年驾驶员的安全路线建…

Redis未授权访问-CNVD-2019-21763复现

Redis未授权访问-CNVD-2019-21763复现 利用项目: https://github.com/vulhub/redis-rogue-getshell 解压后先进入到 RedisModulesSDK目录里面的exp目录下,make编译一下才会产生exp.so文件,后面再利用这个exp.so文件进行远程代码执行 需要p…

Python基础语法之学习字符串格式化

Python基础语法之学习字符串格式化 一、代码二、效果 一、代码 # 通过m.n控制 a 123 b 123.444 c 123.555 print("限制为5:%5d" % a) print("限制为2:%2d" % a) print("限制为5.2:%5.2f" % b) print("限制为5.2:%5.2f" % c)二、效…

高效解决在本地打开可视化服务器端的tensorboard

文章目录 问题解决方案 问题 由于连着远程服务器构建模型,但是想在本地可视化却做不到,不要想当然天真的以为CTRLC点击链接http://localhost:6006就真能在本地打开tensorboard。你电脑都没连接服务器,只是pycharm连上了而已 解决方案 你需要…

全汉电源SN生产日期解读

新买了一个全汉的电脑电源,SN:WZ3191900030,看了几次没想明白,最后估计SN是2023年19周这样来记录日期的。问了一下京东全汉客服,果然就是这样的。那大家如果在闲鱼上看到全汉电源,就知道它的生产日期了。

JS代码其实可以这样写

日常工作中,我确实经常去帮大家review代码,长期以来,我发现有些个功能函数,JS其实可以稍微调整一下,或者换个方式来处理,代码就会看起来更清晰,更简洁,甚至效率更高,主要…

MySQL之 InnoDB逻辑存储结构

InnoDB逻辑存储结构 InnoDB将所有数据都存放在表空间中,表空间又由段(segment)、区(extent)、页(page)组成。InnoDB存储引擎的逻辑存储结构大致如下图。下面我们就一个个来看看。 页&#xff08…

智慧配电间(配电室智能监控)

智慧配电间是一种应用物联网、云计算、大数据等先进技术,对配电室进行智能化改造和升级,依托电易云-智慧电力物联网,实现电力设备的实时监控、智能控制和远程管理的解决方案。以下是智慧配电间的主要功能和特点: 实时监控与数据分…

中式言情短剧APP力压TikTok荣登美国下载榜一!外国人也难逃“霸总爱上我”的狗血剧?

开局退婚、豪门恩怨、霸道总裁爱上我……这些由中国团队拍摄、外国演员出演的竖屏霸总短剧,正在海外收割市场。 01 ReelShort力压TikTok冲上美国榜一 TKFFF获悉,国内数字出版企业中文在线旗下短剧App ReelShort日前力压TikTok冲上美国iOS娱乐榜第1名&…

[Matlab有限元分析] 2.杆单元有限元分析

1. 一维杆单元有限元分析程序 一维刚单元的局部坐标系(单元坐标系)与全局坐标系相同。 1.1 线性杆单元 如图所示是一个杆单元,由两个节点i和j,局部坐标系的X轴沿着杆的方向,由i节点指向j节点,每个节点有…

唯品会年度特卖大会㊙内购清单㊙

唯品会年度特卖大会㊙内购清单㊙ 内部员工亲友专享,实实在在省钱,❌抢完不补! 今晚8点开抢,提前收藏>> https://t.vip.com/Im3KlTnDSJ8 2023年唯品会年度特卖大会热门会场推荐 1.唯品会年度特卖大会 限时加码!瓜分百万津贴!抢海量…

【软件测试】白盒测试和黑盒测试

一、软件测试基本分类 一般地,我们将软件测试活动分为以下几类:黑盒测试、白盒测试、静态测试、动态测试、手动测试、自动测试等等。 黑盒测试 黑盒测试又叫功能测试、数据驱动测试或给予需求规格说明书的功能测试。这种测试注重于测试软件的功能性需…

什么是木马

木马 1. 定义2. 木马的特征3. 木马攻击流程4. 常见木马类型5. 如何防御木马 1. 定义 木马一名来源于古希腊特洛伊战争中著名的“木马计”,指可以非法控制计算机,或在他人计算机中从事秘密活动的恶意软件。 木马通过伪装成正常软件被下载到用户主机&…

【laBVIEW学习】4.声音播放,自定义图标,滚动条设置

一。声音播放(报错,未实现) 1.报错4810 2.解决方法: 暂时未解决。 二。图片修改 1.目标:灯泡---》自定义灯泡 2.步骤: 1.右键点击--》自定义运行 表示可以制作自定义类型 2.右键--》打开自定义类型 这样就…

Python streamlit指南,构建令人惊叹的可视化Web界面!

更多资料获取 📚 个人网站:ipengtao.com 在当今数据驱动的世界中,构建交互式、美观且高效的数据可视化应用变得至关重要。而Streamlit,作为Python生态系统中为开发者提供了轻松创建Web应用的利器。 本文将深入探讨Streamlit的方…

Intellij IDEA 的安装和使用以及配置

IDE有很多种,常见的Eclipse、MyEclipse、Intellij IDEA、JBuilder、NetBeans等。但是这些IDE中目前比较火的是Intellij IDEA(以下简称IDEA),被众多Java程序员视为最好用的Java集成开发环境,今天的主题就是IDEA为开发工…

【模板】KMP算法笔记

练习链接:【模板】KMP - 洛谷 题目: 输入 ABABABC ABA 输出 1 3 0 0 1 思路: 根据题意,用到的是KMP算法,KMP算法思想是通过一个一个匹配首字母的原理进行整个匹配效果,当某个首字母不匹配的时候&#x…

系列十七、各种各样的bean

一、Spring bean 1.1、概述 一句话,被Spring容器管理的bean就是Spring bean。 二、Java bean VS Spring bean 2.1、概述 Java bean是程序员自己new 出来的,Spring bean是Spring工厂创建出来的。 三、配置bean的方式 3.1、概述 所谓配置bean&#xff0…