ホームページ >バックエンド開発 >Golang >[言語] Tracer: Open Telemetry、Golang、Jagger の簡単な実装

[言語] Tracer: Open Telemetry、Golang、Jagger の簡単な実装

Patricia Arquette
Patricia Arquetteオリジナル
2025-01-16 14:14:59547ブラウズ

トレーサー

トレーサーは、マイクロサービス アーキテクチャの実装において重要な役割を果たし、アプリケーション ロジックで実行されているプロセスの「トレース」概要を提供するオブザーバビリティの一部です。

簡単に言うと、Web サービスでは、Tracer は Trace 信号を発行することで、ロジックの実行時間の長さのアイデアを提供します。後で、このトレーサーは、エクスポーターを介してコレクターに信号を送信した後、ネストされたスパンの形式で視覚化され、表示されます。 [OpenTelemetry: トレース]

オープンテレメトリ

後でコレクタによって収集できるトレース信号を送信できるようにするために、Web サービスにはライブラリとして OpenTelemetry が必要です。このライブラリは、通常 OpenTelemetry Protocol (OTLP) と呼ばれる標準の可観測性プロトコルとなっています。 [OpenTelemetry: 言語 - 移動]

イェーガー

Web サービスでどのようなプロセスが発生するかの概要を提供するには、トレース信号の視覚化が実際に必要です。 Yeter は、HTTP または gRPC 通信プロトコルを利用して OTLP をサポートするオープンソース プラットフォームです。 [イェーガー]

Golangでの実装

Golang プログラミング言語での Tracer の実装は、Web サービスが異なる応答期間のデータのみを返す単純なケースを実装します。使用されるライブラリは次のとおりです:

  • チ: HTTP フレームワーク
  • OpenTelemetry: テレメトリ シグナリング

OpenTelemetry を Telemetry モジュールとしてセットアップする

pkg/telemetry/telemetry.go ディレクトリに Telemetry モジュールを実装します。

package telemetry

import (
    "context"
    "errors"
    "time"

    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
    "go.opentelemetry.io/otel/sdk/resource"
    "go.opentelemetry.io/otel/sdk/trace"
)

// enumeration constant for which protocol used
const (
    HTTP uint8 = iota
    GRPC
)

// setup client to connect web-service with Jaeger
func SetupTraceClient(ctx context.Context, protocol uint8, endpoint string) otlptrace.Client {
    switch protocol {
    case GRPC:
        return otlptracegrpc.NewClient(otlptracegrpc.WithEndpoint(endpoint), otlptracegrpc.WithInsecure(), otlptracegrpc.WithCompressor("gzip"))
    default:
        return otlptracehttp.NewClient(otlptracehttp.WithEndpoint(endpoint), otlptracehttp.WithInsecure(), otlptracehttp.WithCompression(otlptracehttp.NoCompression))
    }
}

func setupTraceProvider(ctx context.Context, traceClient otlptrace.Client) (*trace.TracerProvider, error) {
    // set resource
    res, err := resource.New(
        ctx,
        resource.WithFromEnv(),
    )
    if err != nil {
        return nil, err
    }

    // init trace exporter
    traceExporter, err := otlptrace.New(ctx, traceClient)
    if err != nil {
        return nil, err
    }

    // init trace exporter
    traceProvider := trace.NewTracerProvider(
        trace.WithBatcher(
            traceExporter,
            trace.WithBatchTimeout(time.Duration(time.Second*3)),
        ),
        trace.WithResource(res), // Discover and provide attributes from OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME environment variables.
    )

    return traceProvider, nil
}

func SetupTelemetrySDK(ctx context.Context, traceClient otlptrace.Client) (func(context.Context) error, error) {
    var err error
    var shutdownFuncs []func(context.Context) error

    shutdown := func(ctx context.Context) error {
        var err error
        for _, fn := range shutdownFuncs {
            err = errors.Join(err, fn(ctx))
        }
        shutdownFuncs = nil
        return err
    }

    handleErr := func(inErr error) {
        err = errors.Join(inErr, shutdown(ctx))
    }

    traceProvider, err := setupTraceProvider(ctx, traceClient)
    if err != nil {
        handleErr(err)
        return shutdown, err
    }

    shutdownFuncs = append(shutdownFuncs, traceProvider.Shutdown)
    otel.SetTracerProvider(traceProvider)

    return shutdown, nil
}

次に、メイン関数 main.go でテレメトリ構成をセットアップします。

package main

import (
    "context"
    "fmt"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/go-chi/chi/v5"
    "github.com/wahyurudiyan/medium/otel-jaeger/config"
    "github.com/wahyurudiyan/medium/otel-jaeger/pkg/telemetry"
    "github.com/wahyurudiyan/medium/otel-jaeger/router"
)

func SetupTelemetry(ctx context.Context, config *config.Config) (func(context.Context) error, error) {
    otlpCli := telemetry.SetupTraceClient(ctx, telemetry.GRPC, config.JaegerGRPCEndpoint)
    shutdownFn, err := telemetry.SetupTelemetrySDK(ctx, otlpCli)
    return shutdownFn, err
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    cfg := config.Get()

    shutdownFn, err := SetupTelemetry(ctx, cfg)
    if err != nil {
        shutdownFn(ctx)
        panic(err)
    }

    r := chi.NewRouter()
    r.Route("/api", func(r chi.Router) {
        router.Router(r)
    })

    srv := http.Server{
        Addr:    "0.0.0.0:8080",
        Handler: r,
    }
    go func() {
        fmt.Println("Server running at port:", srv.Addr)
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            fmt.Printf("listen: %s\n", err)
        }
    }()

    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    



<p>router/router.go ファイルのハンドラーで Tracer を使用して、トレース信号を送信します。<br>
</p>

<pre class="brush:php;toolbar:false">package router

import (
    "encoding/json"
    "net/http"
    "time"

    "github.com/go-chi/chi/v5"
    "github.com/wahyurudiyan/medium/otel-jaeger/pkg/random"
    "go.opentelemetry.io/otel"
)

var (
    tracer = otel.Tracer("WebServer-Otel-Jaeger")
)

func getUserHandler(w http.ResponseWriter, r *http.Request) {
    _, span := tracer.Start(r.Context(), "GetUser")
    defer span.End()

    user := struct {
        Name     string
        Email    string
        Password string
    }{
        Name:     "John Doe",
        Email:    "john@email.com",
        Password: "Super5ecr3t!",
    }
    blob, _ := json.Marshal(&user)

    sleepDuration := time.Duration(time.Millisecond * time.Duration(random.GenerateRandNum()))
    time.Sleep(sleepDuration)

    w.Header().Add("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    w.Write(blob)
}

func Router(router chi.Router) {
    router.Get("/user", getUserHandler)
}

導入

Dockerfile のマルチステージ ビルド イメージ メカニズムを利用した、このビルド Web サービスの Docker 構成:

FROM golang:1.23.4 AS build
WORKDIR /src
COPY . .
RUN go get -v
RUN CGO_ENABLED=0 go build -o /bin/service .

FROM alpine:latest
WORKDIR /app
COPY --from=build /bin/service /bin/service
CMD ["/bin/service"]

次に、次の構成で docker-compose.yaml ファイルを介してイメージのビルドが実行されます。

services:
  web-service:
    container_name: service
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      OTEL_SERVICE_NAME: service-otel-jaeger
      JAEGER_GRPC_ENDPOINT: jaeger:4317
    entrypoint: ["service"]
    ports:
      - 8080:8080

  jaeger:
    container_name: jaeger
    image: jaegertracing/all-in-one:latest
    environment:
      COLLECTOR_ZIPKIN_HOST_PORT: :9411
    ports:
      - 16686:16686 
      - 4317:4317 
      - 4318:4318 
      - 9411:9411

service.jaeger.ports で公開されているポートは次のポートです:

  • 16686: イェーガー ダッシュボード
  • 4317: gRPC プロトコルを使用したJaeger OTLP Protobuf
  • 4318: HTTP プロトコルを使用したJaeger OTLP Protobuf/JSON
  • 9411: ジップキンコレクター

docker-compose.yaml で定義されているアプリケーションを実行します。次のコマンドを使用できます。

docker compose up --build

アプリケーションの実行後、エンドポイント http://127.0.0.1:8080/api/user でアプリケーションを実行してみてください。Web サービスとアプリケーションが接続されている場合は、図のようにサービス名が表示されます。

[言語] Tracer: Open Telemetry、Golang、Jagger の簡単な実装

プロセスの実行にかかる時間を定義するためのスパンが表示されます。

[言語] Tracer: Open Telemetry、Golang、Jagger の簡単な実装

負荷テスト

次に、CLI ツール hey [https://github.com/rakyll/hey] を使用して負荷テストを実行してみましょう。次のコマンドを使用して、簡単な負荷テストを実行できます:

package telemetry

import (
    "context"
    "errors"
    "time"

    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
    "go.opentelemetry.io/otel/sdk/resource"
    "go.opentelemetry.io/otel/sdk/trace"
)

// enumeration constant for which protocol used
const (
    HTTP uint8 = iota
    GRPC
)

// setup client to connect web-service with Jaeger
func SetupTraceClient(ctx context.Context, protocol uint8, endpoint string) otlptrace.Client {
    switch protocol {
    case GRPC:
        return otlptracegrpc.NewClient(otlptracegrpc.WithEndpoint(endpoint), otlptracegrpc.WithInsecure(), otlptracegrpc.WithCompressor("gzip"))
    default:
        return otlptracehttp.NewClient(otlptracehttp.WithEndpoint(endpoint), otlptracehttp.WithInsecure(), otlptracehttp.WithCompression(otlptracehttp.NoCompression))
    }
}

func setupTraceProvider(ctx context.Context, traceClient otlptrace.Client) (*trace.TracerProvider, error) {
    // set resource
    res, err := resource.New(
        ctx,
        resource.WithFromEnv(),
    )
    if err != nil {
        return nil, err
    }

    // init trace exporter
    traceExporter, err := otlptrace.New(ctx, traceClient)
    if err != nil {
        return nil, err
    }

    // init trace exporter
    traceProvider := trace.NewTracerProvider(
        trace.WithBatcher(
            traceExporter,
            trace.WithBatchTimeout(time.Duration(time.Second*3)),
        ),
        trace.WithResource(res), // Discover and provide attributes from OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME environment variables.
    )

    return traceProvider, nil
}

func SetupTelemetrySDK(ctx context.Context, traceClient otlptrace.Client) (func(context.Context) error, error) {
    var err error
    var shutdownFuncs []func(context.Context) error

    shutdown := func(ctx context.Context) error {
        var err error
        for _, fn := range shutdownFuncs {
            err = errors.Join(err, fn(ctx))
        }
        shutdownFuncs = nil
        return err
    }

    handleErr := func(inErr error) {
        err = errors.Join(inErr, shutdown(ctx))
    }

    traceProvider, err := setupTraceProvider(ctx, traceClient)
    if err != nil {
        handleErr(err)
        return shutdown, err
    }

    shutdownFuncs = append(shutdownFuncs, traceProvider.Shutdown)
    otel.SetTracerProvider(traceProvider)

    return shutdown, nil
}

このコマンドは、1 秒あたり 100 リクエスト (RPS) の負荷テストを 10 分間実行します。 Jaeger UI ページに表示される結果は次のようになります。

[言語] Tracer: Open Telemetry、Golang、Jagger の簡単な実装

負荷テストの実行が完了すると、負荷テストの結果のレポートが表示されます。

package main

import (
    "context"
    "fmt"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/go-chi/chi/v5"
    "github.com/wahyurudiyan/medium/otel-jaeger/config"
    "github.com/wahyurudiyan/medium/otel-jaeger/pkg/telemetry"
    "github.com/wahyurudiyan/medium/otel-jaeger/router"
)

func SetupTelemetry(ctx context.Context, config *config.Config) (func(context.Context) error, error) {
    otlpCli := telemetry.SetupTraceClient(ctx, telemetry.GRPC, config.JaegerGRPCEndpoint)
    shutdownFn, err := telemetry.SetupTelemetrySDK(ctx, otlpCli)
    return shutdownFn, err
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    cfg := config.Get()

    shutdownFn, err := SetupTelemetry(ctx, cfg)
    if err != nil {
        shutdownFn(ctx)
        panic(err)
    }

    r := chi.NewRouter()
    r.Route("/api", func(r chi.Router) {
        router.Router(r)
    })

    srv := http.Server{
        Addr:    "0.0.0.0:8080",
        Handler: r,
    }
    go func() {
        fmt.Println("Server running at port:", srv.Addr)
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            fmt.Printf("listen: %s\n", err)
        }
    }()

    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    



<h2>
  
  
  Github プロジェクト
</h2>

<p>完全なコードを試したり確認したい場合は、次のリポジトリのクローンを作成できます: https://github.com/wahyurudiyan/otel-jaeger.</p>


          

            
        

以上が[言語] Tracer: Open Telemetry、Golang、Jagger の簡単な実装の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。