通过示例学习:第10部分 - 使用OpenTelemetry仪器进行GO应用程序,然后将痕迹发送到Jaeger-分布式跟踪
#初学者 #go #opentelemetry #tracing

在以前的文章中,我们创建了一个HTTP REST API server,一个CLI,一个Bot for Discord,甚至是game for Nintendo Game Boy Advance

今天,我们将学习如何使用opentelemetry go库来创建仪器应用程序并将轨迹发送到jaeger实例。

opentelemetry

OpenTelemetry

OpenTelemetry是工具,API和SDK的集合。对仪器,生成,收集和导出遥测数据(指标,日志和痕迹)有用,可帮助您分析软件的性能和行为。

opentelemetry与流行的库和框架(例如春季,express,quarkus和with a lot of languages)集成,包括: - )。

OpenTracing

如果您曾经听说过OpenTracing或习惯了使用它,请知道现在已经弃用了opentracing,因此最好使用opentelemetryð。
如果您想migrate from OpenTracing to OpenTelemetry,则存在官方指南。

Jaeger

Jaeger

Jaeger是一个开源分布式跟踪平台。

它可用于监视基于微服务的分布式系统:

  • 分布式上下文传播
  • 分布式交易监控
  • 根本原因分析
  • 服务依赖分析
  • 性能 /延迟优化< / li>

jaeger包含多个组件:

Jaeger

本地运行Jaeger

我们将使用Docker运行Jaeger UI,收集器,查询和代理,并具有内存存储组件:

$ docker run -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
  -p 5775:5775/udp \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 14268:14268 \
  -p 9411:9411 \
  jaegertracing/all-in-one:1.6

Jaeger UI将在16686端口上可用,您将向14268发送跟踪。

打开浏览器,输入url http://localhost:16686以显示jaeger ui:

Jaeger UI

初始化

我们在上一篇文章中创建了GIT存储库,因此现在我们只需要在本地检索它:

$ git clone https://github.com/scraly/learning-go-by-examples.git
$ cd learning-go-by-examples

我们将为我们的CLI应用程序创建一个文件夹go-gopher-opentelemetry,然后进入:

$ mkdir go-gopher-opentelemetry
$ cd go-gopher-opentelemetry

现在,我们必须初始化GO模块(依赖关系管理):

$ go mod init github.com/scraly/learning-go-by-examples/go-gopher-opentelemetry
go: creating new go.mod: module github.com/scraly/learning-go-by-examples/go-gopher-opentelemetry

这将创建一个这样的go.mod文件:

module github.com/scraly/learning-go-by-examples/go-gopher-opentelemetry

go 1.19

在开始我们的超级仪器应用程序之前,我们将创建一个简单的代码组织。

创建以下文件夹组织:

.
├── README.md
├── bin
└── go.mod

就是这样吗?是的,我们的其余代码组织将很快创建; - )。

创建我们的应用程序

opentelemetry分为两个部分:与仪器代码的API,以及实现API的SDK。

让我们安装OpenTelemetry Trace API,以便在我们的代码中使用它:

$ go get go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
$ go get go.opentelemetry.io/otel/exporters/jaeger
$ go get go.opentelemetry.io/otel/sdk/resource
$ go get go.opentelemetry.io/otel/sdk/trace

好,现在我们可以创建一个main.go文件并将以下代码复制到其中。

GO代码被组织成软件包。因此,首先,我们初始化了称为main的软件包,以及我们需要在主文件中导入和使用的所有依赖关系/库:

package main

import (
    "context"
    "fmt"
    "log"
    "net/http"
    "os"
    "time"

    "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
    "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.7.0"
)

添加了进口,您可以启动仪器。

OpentElemetry Tracing API提供了创建痕迹的示踪剂。这些示踪剂的设计与一个仪表库相关联。要唯一地识别示踪剂的应用程序,我们将使用main.go文件中的软件包名称创建常数。

所以,我们定义const:

const (
    service     = "go-gopher-opentelemetry"
    environment = "development"
    id          = 1
)

然后,我们创建一个称为tracerProvider()的函数,该函数启动了与示踪剂提供商的连接,例如Jaeger实例。

func tracerProvider(url string) (*tracesdk.TracerProvider, error) {
    // Create the Jaeger exporter
    exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
    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.ServiceNameKey.String(service),
            attribute.String("environment", environment),
            attribute.Int64("ID", id),
        )),
    )
    return tp, nil
}

main()函数:

  • 连接到您先前部署的Jaeger Collector
  • 创建在port 8080上侦听的HTTP服务器
  • 并每次调用 / http路由时都会创建并发送跨度:
func main() {

    // Tracer
    tp, err := tracerProvider("http://localhost:14268/api/traces")
    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, "hello")
    defer span.End()

    // HTTP Handlers
    helloHandler := func(w http.ResponseWriter, r *http.Request) {
        // Use the global TracerProvider
        tr := otel.Tracer("hello-handler")
        _, span := tr.Start(ctx, "hello")
        span.SetAttributes(attribute.Key("mykey").String("value"))
        defer span.End()

        yourName := os.Getenv("MY_NAME")
        fmt.Fprintf(w, "Hello %q!", yourName)
    }

    otelHandler := otelhttp.NewHandler(http.HandlerFunc(helloHandler), "Hello")

    http.Handle("/", otelHandler)

    log.Println("Listening on localhost:8080")

    log.Fatal(http.ListenAndServe(":8080", nil))
}

当调用http路由时,我们在“ my_name”环境变量中打印一条消息,并用“ hello”和您的名字。

让我们在本地测试

首先,在本地,我们必须定义环境变量并运行您的应用程序:

$ export MY_NAME=scraly ; go run main.go
2022/11/08 19:07:54 Listening on localhost:8080

在我们的HTTP服务器中拨打电话:

$ curl localhost:8080
Hello "scraly"!

让我们再次观看Jaeger UI。很酷,出现了新的go-gopher-opentelemetry服务。选择它,然后单击Find Traces按钮:

Jaeger new span

您现在可以单击跟踪并可视化有用的信息。

trace

在跟踪中,您可以在痕迹中看到我们定义的environementid常数。

结论

正如您在本文和以前的文章中所看到的那样,可以在GO中创建应用程序:CLI,REST API ...以及有用的应用程序,可以使用分布式跟踪,您可以链接到其他工具。

我们的仪器应用程序的所有代码均提供:https://github.com/scraly/learning-go-by-examples/tree/main/go-gopher-opentelemetry

在以下文章中,我们将在GO中创建其他应用程序/类型的应用程序。

希望您会喜欢的。