Rumah >pembangunan bahagian belakang >Golang >[Bahasa] Pengesan: Telemetri Terbuka, Golang dan Pelaksanaan Mudah Jagger

[Bahasa] Pengesan: Telemetri Terbuka, Golang dan Pelaksanaan Mudah Jagger

Patricia Arquette
Patricia Arquetteasal
2025-01-16 14:14:59548semak imbas

Pengesan

Tracer ialah sebahagian daripada Kebolehmerhatian yang memainkan peranan penting dalam pelaksanaan Seni Bina Perkhidmatan Mikro dan menyediakan gambaran keseluruhan 'Jejak' proses yang dijalankan dalam logik aplikasi.

Ringkasnya, dalam Perkhidmatan Web, Tracer akan memberikan idea tentang berapa lama masa pelaksanaan logik dengan memancarkan isyarat Trace. Kemudian, Tracer ini akan divisualisasikan dan dilihat dalam bentuk rentang bersarang selepas menghantar isyarat melalui Pengeksport kepada Pemungut. [OpenTelemetry: Jejak]

OpenTelemetry

Untuk dapat menghantar isyarat Jejak yang kemudiannya boleh dikumpulkan oleh Pemungut, Perkhidmatan Web memerlukan OpenTelemetry sebagai perpustakaan yang telah menjadi protokol Kebolehmerhatian standard yang biasanya dipanggil OpenTelemetry Protocol (OTLP). [OpenTelemetry: Language - Go]

Jaeger

Visualisasi isyarat Jejak benar-benar diperlukan untuk memberikan gambaran keseluruhan proses yang berlaku dalam Perkhidmatan Web. Jaeger ialah platform Sumber Terbuka yang menyokong OTLP dengan menggunakan protokol komunikasi HTTP atau gRPC. [Jaeger]

Pelaksanaan di Golang

Pelaksanaan Tracer dalam bahasa pengaturcaraan Golang akan melaksanakan kes mudah di mana Perkhidmatan Web hanya akan mengembalikan data dengan tempoh respons yang berbeza. Perpustakaan yang akan digunakan ialah:

  • Chi: Rangka Kerja HTTP
  • OpenTelemetry: Isyarat Telemetri

Sediakan OpenTelemetry sebagai modul Telemetri

Laksanakan modul Telemetri dalam direktori pkg/telemetri/telemetri.go:

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
}

Kemudian, sediakan konfigurasi Telemetri dalam fungsi utama 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>Gunakan Tracer dalam pengendali dalam fail router/router.go untuk menghantar isyarat Traces:<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)
}

Kerahan

Konfigurasi Docker untuk Perkhidmatan Web Binaan ini, menggunakan mekanisme Imej Binaan Berbilang Peringkat dalam Fail 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"]

Seterusnya, binaan imej akan dijalankan melalui fail docker-compose.yaml dengan konfigurasi berikut:

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

Dalam service.jaeger.ports, port yang terdedah ialah port untuk:

  • 16686: Papan Pemuka Jaeger
  • 4317: Jaeger OTLP Protobuf dengan protokol gRPC
  • 4318: Jaeger OTLP Protobuf/JSON dengan protokol HTTP
  • 9411: Pengumpul Zipkin

Jalankan aplikasi yang telah ditakrifkan dalam docker-compose.yaml, anda boleh menggunakan arahan:

docker compose up --build

Selepas aplikasi berjalan, anda boleh cuba menekan aplikasi pada titik akhir http://127.0.0.1:8080/api/user, jika perkhidmatan Web dan aplikasi disambungkan, nama perkhidmatan akan muncul seperti dalam gambar.

[Bahasa] Pengesan: Telemetri Terbuka, Golang dan Pelaksanaan Mudah Jagger

span akan muncul untuk menentukan tempoh masa yang diperlukan untuk menjalankan proses.

[Bahasa] Pengesan: Telemetri Terbuka, Golang dan Pelaksanaan Mudah Jagger

Ujian Beban

Sekarang mari cuba gunakan alat CLI hey [https://github.com/rakyll/hey] untuk menjalankan ujian beban. Perintah berikut boleh digunakan untuk melaksanakan ujian beban mudah:

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
}

Arahan akan menjalankan ujian beban untuk 100 permintaan sesaat (RPS) selama 10 minit. Keputusan yang akan dipaparkan pada halaman UI Jaeger akan kelihatan seperti berikut.

[Bahasa] Pengesan: Telemetri Terbuka, Golang dan Pelaksanaan Mudah Jagger

Jika ujian beban telah selesai dijalankan, akan ada laporan keputusan ujian beban.

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>
  
  
  Projek Github
</h2>

<p>Bagi mereka yang ingin mencuba atau melihat kod penuh, anda boleh mengklon repositori berikut: https://github.com/wahyurudiyan/otel-jaeger.</p>


          

            
        

Atas ialah kandungan terperinci [Bahasa] Pengesan: Telemetri Terbuka, Golang dan Pelaksanaan Mudah Jagger. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn