Go 鏈路追蹤入門 Opentelemetry

灯火消逝的码头發表於2024-08-15

前言

Opentelemetry

分散式鏈路跟蹤( Distributed Tracing )的概念最早是由 Google 提出來的,發展至今技術已經比較成熟,也是有一些協議標準可以參考。目前在 Tracing技術這塊比較有影響力的是兩大開源技術框架:Netflix 公司開源的 OpenTracing 和 Google 開源的 OpenCensus。兩大框架都擁有比較高的開發者群體。為形成統一的技術標準,兩大框架最終磨合成立了 OpenTelemetry 專案,簡稱 otel。otel 有鏈路追蹤和監控告警兩大塊,關於監控告警,可以檢視另一篇文章:Go 監控告警入門 Opentelemetry

Jaeger

Jaeger\ˈyā-gər\ 是 Uber 開源的分散式追蹤系統,是支援 OpenTelemetry 的系統之一,也是 CNCF 專案。

安裝 Jaeger

Jaeger 為我們準備了 Docker 映象,我們可以很容易的安裝。

docker run --rm --name jaeger \
  -p 16686:16686 \
  -p 4317:4317 \
  -p 4318:4318

截至到 2024-04-18, Jaeger 的最新版本是 1.56。

簡單的介紹以下這三個埠。16686 用做 Jaeger 服務的 Web 皮膚,一會我們可以在瀏覽器中訪問它;4317 和 4318 都用做上傳追蹤資料,不同之處在於前者是 gRPC 協議,後者是 HTTP 協議。

Jaeger 還有很多可用的埠,本篇只介紹和 otel 相關的,具體可以檢視 Jaeger 官方文件哦。

安裝後,在瀏覽器中輸入 IP:16686:

Go 鏈路追蹤入門 Opentelemetry

看到 gopher 偵探在追蹤足跡的可愛圖片就代表 Jaeger 安裝成功咯。

編寫 Go 程式碼

安裝依賴:

go get "go.opentelemetry.io/otel" \
  "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" \
  "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" \
  "go.opentelemetry.io/otel/propagation" \
  "go.opentelemetry.io/otel/sdk/metric" \
  "go.opentelemetry.io/otel/sdk/resource" \
  "go.opentelemetry.io/otel/sdk/trace" \
  "go.opentelemetry.io/otel/semconv/v1.24.0" \
  "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"

我這裡貼出 HTTP 和 gRPC 的全部程式碼,直接複製過去,改成自己的地址即可:

HTTP

func TestTraceHttp(t *testing.T) {
	ctx := context.Background()

	// 建立 OTLP HTTP 匯出器,連線到 Jaeger
	exporter, err := otlptracehttp.New(ctx,
		otlptracehttp.WithEndpointURL("http://srv.com:4318/v1/traces"))

	if err != nil {
		log.Fatalf("建立匯出器失敗: %v", err)
	}

	// 建立資源
	res, err := resource.New(ctx,
		resource.WithAttributes(
			semconv.ServiceNameKey.String("otel-traces-demo-http"),
		),
	)
	if err != nil {
		log.Fatalf("建立資源失敗: %v", err)
	}

	// 建立 Tracer 提供器
	tp := sdktrace.NewTracerProvider(
		sdktrace.WithBatcher(exporter),
		sdktrace.WithResource(res),
	)

	// 設定全域性 Tracer 提供器
	otel.SetTracerProvider(tp)

	// 建立一個新的 trace
	tracer := otel.Tracer("example-tracer")
	ctx, span := tracer.Start(ctx, "root-span")
	// 暫停 100ms
	time.Sleep(100 * time.Millisecond)
	// 結束 span
	span.End()

	// 建立子span
	_, childSpan := tracer.Start(ctx, "child-span")
	// 暫停 50ms
	time.Sleep(50 * time.Millisecond)
	childSpan.End()

	// 確保所有的 spans 都被髮送
	if err := tp.Shutdown(ctx); err != nil {
		log.Fatalf("關閉 Tracer 提供器失敗: %v", err)
	}
}

gRPC

func TestTraceGrpc(t *testing.T) {
	ctx := context.Background()

	// 建立 OTLP gRPC 匯出器,連線到 Jaeger
	exporter, err := otlptracegrpc.New(ctx,
		otlptracegrpc.WithEndpoint("srv.com:4317"),
		otlptracegrpc.WithInsecure(),
	)

	if err != nil {
		log.Fatalf("建立匯出器失敗: %v", err)
	}

	// 建立資源
	res, err := resource.New(ctx,
		resource.WithAttributes(
			semconv.ServiceNameKey.String("otel-traces-demo-grpc"),
		),
	)
	if err != nil {
		log.Fatalf("建立資源失敗: %v", err)
	}

	// 建立 Tracer 提供器
	tp := sdktrace.NewTracerProvider(
		sdktrace.WithBatcher(exporter),
		sdktrace.WithResource(res),
	)

	// 設定全域性 Tracer 提供器
	otel.SetTracerProvider(tp)

	// 建立一個新的 trace
	tracer := otel.Tracer("example-tracer")
	ctx, span := tracer.Start(ctx, "root-span")
	// 暫停 100ms
	time.Sleep(100 * time.Millisecond)
	// 結束 span
	span.End()

	// 建立子span
	_, childSpan := tracer.Start(ctx, "child-span")
	// 暫停 50ms
	time.Sleep(50 * time.Millisecond)
	childSpan.End()

	// 確保所有的 spans 都被髮送
	if err := tp.Shutdown(ctx); err != nil {
		log.Fatalf("關閉 Tracer 提供器失敗: %v", err)
	}
}

效果

執行後,在皮膚中即可看到我們上傳的資料。

Go 鏈路追蹤入門 Opentelemetry

Go 鏈路追蹤入門 Opentelemetry

可以看到我們的兩個 span 已經上傳到 Jaeger 中了,就是如此的簡單!文中的程式碼開源在 Github

相關文章