• 如何在 Golang 中使用 OpenTelemetry 进行链路追踪?
  • 发布于 2个月前
  • 682 热度
    0 评论
前言
在当今云原生应用程序的世界中,链路追踪成为了一项至关重要的任务。链路追踪可以帮助开发人员理解分布式系统中各个组件之间的交互,并帮助他们定位和解决问题。在本文中,我们将介绍如何在 Golang 中使用 OpenTelemetry 进行链路追踪。

OpenTelemetry 简介
OpenTelemetry 是一个云原生的分布式追踪、日志和度量的规范与实现。它是由多个开源项目合并而成的,并受到了许多组织和公司的支持。OpenTelemetry 提供了一个用于收集、分析和导出跨多个服务的跟踪数据的标准化框架,使得开发人员可以更轻松地理解分布式系统中各个组件之间的交互。

链路追踪的基础知识
在深入讨论如何使用 OpenTelemetry 进行链路追踪之前,让我们先讨论一些基础知识。什么是链路追踪?为什么它很重要?如何实现链路追踪?链路追踪是一种用于记录和跟踪请求在分布式系统中经过的各个组件的技术。对于大型的分布式系统,请求通常会经过多个服务和组件,每个组件都会对请求进行处理并将其传递给下一个组件。链路追踪可以帮助开发人员在整个系统中跟踪请求的流程,并找出性能瓶颈和故障。

在链路追踪中,一个请求被称为一条链路。链路由一系列称为 Span 的记录组成,每个 Span 都代表着请求在分布式系统中经过的一段时间。Span 包含了有关请求的信息,如开始时间、结束时间、服务名称、操作名称、父 Span 和子 Span 等等。为了实现链路追踪,需要在代码中插入一些记录信息的代码。这些记录信息的代码被称为 Tracer。Tracer 可以记录请求的开始和结束时间,并生成 Span。通常,每个服务都会有自己的 Tracer 实例。

在 Golang 中使用 OpenTelemetry
现在,让我们看看如何在 Golang 中使用 OpenTelemetry 进行链路追踪。为了使用 OpenTelemetry,我们需要安装 OpenTelemetry SDK 和一些适配器。我们可以使用以下命令安装:
go get go.opentelemetry.io/otel/sdk/trace
go get go.opentelemetry.io/otel/exporters/stdout
在我们的应用程序中,我们需要创建一个 TracerProvider。这个 TracerProvider 负责管理所有的 Tracer 实例,并将 Span 导出到我们选择的后端。我们可以使用以下代码创建一个 TracerProvider:
import (
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/stdout"
    "go.opentelemetry.io/otel/sdk/resource"
    "go.opentelemetry.io/otel/sdk/trace"
)

func initTracer() {
  // 堆代码 duidaima.com
  // 创建一个 StdoutExporter,用于将 Span 导出到 stdout
  exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
  if err != nil {
    log.Fatal(err)
  }

  // 创建一个 Resource 对象,用于指定应用程序或服务端的名称和版本号
  res, err := resource.New(
    context.Background(),
    resource.WithAttributes(
      semconv.ServiceNameKey.String("my-service"),
      semconv.ServiceVersionKey.String("1.0.0"),
    ),
  )
  if err != nil {
    log.Fatal(err)
  }

  // 创建一个 TraceProvider,用于管理所有的 Tracer 实例
  tp := sdktrace.NewTracerProvider(
    sdktrace.WithSampler(sdktrace.AlwaysSample()),
    sdktrace.WithResource(res),
    sdktrace.WithBatcher(exporter),
  )

  // 注册全局的 TraceProvider
  otel.SetTracerProvider(tp)
}
上面的代码中,我们创建了一个 StdoutExporter,它将 Span 导出到 stdout。我们还创建了一个 Resource 对象,用于指定应用程序的名称和版本号。最后,我们创建了一个 TraceProvider,并将它注册为全局的 TraceProvider。

接下来,我们需要创建一个 Tracer。我们可以使用以下代码创建一个 Tracer:
import (
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/trace"
)

func useOtel() (ctx context.Context) {
  // 堆代码 duidaima.com
  // 创建一个 Tracer 对象,用于记录此次请求的 Span
  // 方式1:
  // tp := otel.GetTracerProvider()
  // tracer := tp.Tracer("my-service")
  // 方式2:
  tracer := otel.Tracer("my-service")


  // 创建一个 SpanContext,用于跟踪请求的父 Span
  ctx = context.Background()

  spanName := "useOtel"
  // 创建一个新的 Span,用于记录此次请求
  curCtx, span := tracer.Start(
    ctx,
    spanName,
    trace.WithSpanKind(trace.SpanKindInternal),
    trace.WithAttributes(
      attribute.String("http.method", "GET"),
      attribute.String("http.host", "www.test.com"),
    ),
  )

  defer span.End()
  // do something
  callOtherFunc(curCtx)

  return
}


func callOtherFunc(parentCtr context.Context) {
  tracer := otel.Tracer("my-service")
  spanName := "callOtherFunc"
  _, span := tracer.Start(
    parentCtr,
    spanName,
  )
  defer span.End()

  // do something
}
上面的代码中,我们使用 otel.Tracer() 函数创建一个 Tracer 对象,用于记录此次请求的 Span。我们还创建了一个 Context,用于跟踪请求,并将其作为参数传递给 Span(tracer.Start 方法)。

不同于其他语言,Go 中跟踪请求链路,需要通过 Context 传递 trace 信息。因此,每一个需要记录 Span 的方法或函数,都需要接收一个 Context 参数,例如 callOtherFunc。

在创建 Span 的时候,我们还可以记录一些其他信息,例如请求的 HTTP 方法、域名等。最后,在方法结束后,我们需要通过 span.End() 结束当前 Span。注意,一定要调用 span.End() 方法,对当前的 Span 耗时进行统计等,否则链路记录不到这次 Span。

最后
现在我们已经完成了在 Go 中使用 OpenTelemetry 进行链路追踪的基本示例。我们可以使用上述代码作为起点,根据自己的需要进行扩展和定制。例如,我们可以使用 JaegerExporter 将 Span 导出到 Jaeger,或者使用 ZipkinExporter 将 Span 导出到 Zipkin,这样就可以在 Jaeger 或 Zipkin 以可视化的方式查看到链路详情。我们还可以使用自定义 Instrumentation 对象记录自定义指标。

总之,OpenTelemetry 是一个非常强大和灵活的工具,可以帮助我们追踪分布式应用程序中的请求并分析性能问题。我希望这篇文章能够帮助您开始使用 OpenTelemetry 进行链路追踪,并为您的应用程序带来更好的可观测性。
用户评论