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]
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]
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 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:
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) }
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:
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.
span akan muncul untuk menentukan tempoh masa yang diperlukan untuk menjalankan proses.
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.
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!