Configura il plug-in OpenTelemetry di base in gRPC Java

1. Introduzione

In questo codelab, utilizzerai gRPC per creare un client e un server che costituiscono la base di un'applicazione di mappatura delle route scritta in Java.

Al termine del tutorial, avrai una semplice applicazione gRPC HelloWorld instrumentata con il plug-in gRPC OpenTelemetry e potrai visualizzare le metriche di osservabilità esportate in Prometheus.

Obiettivi didattici

  • Come configurare il plug-in OpenTelemetry per un'applicazione Java gRPC esistente
  • Esecuzione di un'istanza Prometheus locale
  • Esportazione delle metriche in Prometheus
  • Visualizzazione delle metriche dalla dashboard di Prometheus

2. Prima di iniziare

Che cosa ti serve

  • git
  • curl
  • JDK versione 8 o successive

Installa i prerequisiti:

sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git curl

Ottieni il codice

Per semplificare l'apprendimento, questo codelab offre uno scaffold di codice sorgente precompilato per aiutarti a iniziare. I passaggi seguenti ti guideranno nell'instrumentazione del plug-in gRPC OpenTelemetry in un'applicazione.

grpc-codelabs

Il codice sorgente dello scaffold per questo codelab è disponibile in questa directory di GitHub . Se preferisci non implementare il codice autonomamente, il codice sorgente completato è disponibile nella directory completed.

Innanzitutto, clona il repository del codelab gRPC e passa alla cartella grpc-java-opentelemetry:

git clone -b v1 https://github.com/grpc-ecosystem/grpc-codelabs.git
cd grpc-codelabs/codelabs/grpc-java-opentelemetry/

In alternativa, puoi scaricare il file .zip contenente solo la directory del codelab ed estrarlo manualmente.

3. Registra il plug-in OpenTelemetry

Abbiamo bisogno di un'applicazione gRPC per aggiungere il plug-in gRPC OpenTelemetry. In questo codelab, utilizzeremo un semplice client e server gRPC HelloWorld che instrumenteremo con il plug-in gRPC OpenTelemetry.

Il primo passaggio consiste nel registrare il plug-in OpenTelemetry configurato con un esportatore Prometheus nel client. Apri codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryClient.java con il tuo editor preferito, quindi modifica main per aggiungere il codice per configurare l'API OpenTelemetry Java gRPC.

Configura l'instrumentazione sul client

Crea l'esportatore Prometheus

Crea un PrometheusHttpServer per convertire le metriche OpenTelemetry nel formato Prometheus ed esporle tramite un HttpServer. Il seguente snippet di codice crea un nuovo esportatore Prometheus.

// Default prometheus port i.e `prometheusPort` has been initialized to 9465
 
PrometheusHttpServer prometheusExporter = PrometheusHttpServer.builder()
        .setPort(prometheusPort)         
        .build();

Crea l'istanza di OpenTelemetry SDK

Registra prometheusExporter creato sopra come MetricReader per leggere le metriche da un SdkMeterProvider. SdkMeterProvider viene utilizzato per configurare le impostazioni delle metriche.

SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
        .registerMetricReader(prometheusExporter)
        .build();

Crea un'istanza di OpenTelemetrySdk con sdkMeterProvider creato sopra per l'implementazione dell'SDK di OpenTelemetry.

OpenTelemetrySdk openTelemetrySdk =OpenTelemetrySdk.builder()
        .setMeterProvider(sdkMeterProvider)
        .build();

Crea l'istanza di GrpcOpenTelemetry

Utilizzando l'API GrpcOpenTelemetry API, imposta l'SDK OpenTelemetry che utilizza l'esportatore di metriche Prometheus.

GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
        .sdk(openTelemetrySdk)
        .build();

// Registers gRPC OpenTelemetry globally.
grpcOpenTelmetry.registerGlobal();

Una volta registrata a livello globale un'istanza di GrpcOpenTelemetry utilizzando registerGlobal, tutti i client e i server gRPC creati successivamente verranno instrumentati con OpenTelemetry.

Arresta l'SDK OpenTelemetry

L'arresto deve avvenire all'interno di ShutDownHook. openTelemetrySdk.close() arresta l'SDK e chiama anche l'arresto su SdkMeterProvider.

Configura l'instrumentazione sul server

Allo stesso modo, aggiungiamo GrpcOpenTelemetry anche al server. Apri codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryServer.java e aggiungi il codice per inizializzare GrpcOpenTelemetry.

Crea l'esportatore Prometheus

Poiché questo codelab potrebbe essere eseguito dalla stessa macchina, utilizziamo una porta diversa per ospitare le metriche lato server gRPC per evitare conflitti di porte durante la creazione di PrometheusHttpServer.

// Default prometheus port i.e `prometheusPort` has been set to 9464

PrometheusHttpServer prometheusExporter = PrometheusHttpServer.builder()
        .setPort(prometheusPort)
        .build();

Crea l'istanza di OpenTelemetry SDK

SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
        .registerMetricReader(prometheusExporter)
        .build();

Inizializza GrpcOpenTelemetry con OpenTelemetry SDK

OpenTelemetrySdk openTelemetrySdk =OpenTelemetrySdk.builder()
        .setMeterProvider(sdkMeterProvider)
        .build();

Crea l'istanza di GrpcOpenTelemetry

GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
        .sdk(openTelemetrySdk)
        .build();
    // Registers gRPC OpenTelemetry globally.
grpcOpenTelmetry.registerGlobal();

Arresta l'SDK OpenTelemetry

Dopo l'arresto del canale gRPC. La chiamata di openTelemetrySdk.close() arresta l'SDK e chiama anche l'arresto su SdkMeterProvider.

4. Esecuzione dell'esempio e visualizzazione delle metriche

Per eseguire il server, esegui -

cd start_here
../gradlew installDist
./build/install/start_here/bin/opentelemetry-server

Se la configurazione è andata a buon fine, vedrai il seguente output per il server -

[date and time] io.grpc.codelabs.opentelemetry.OpenTelemetryServer start
INFO: Server started, listening on 50051

Mentre il server è in esecuzione, in un altro terminale esegui il client -

./build/install/start_here/bin/opentelemetry-client world

Un'esecuzione riuscita avrà il seguente aspetto -

[date and time]io.grpc.codelabs.opentelemetry.OpenTelemetryClient greet
INFO: Greeting: Hello world 
[date and time] io.grpc.codelabs.opentelemetry.OpenTelemetryClient greet
INFO: Will try to greet world ...
[date and time]io.grpc.codelabs.opentelemetry.OpenTelemetryClient greet
INFO: Greeting: Hello world

Poiché abbiamo configurato il plug-in gRPC OpenTelemetry per esportare le metriche utilizzando Prometheus. Queste metriche saranno disponibili su localhost:9464 per il server e localhost:9465 per il client.

Per visualizzare le metriche del client -

curl localhost:9465/metrics

Il risultato sarà del modulo -

# HELP grpc_client_attempt_duration_seconds Time taken to complete a client call attempt
# TYPE grpc_client_attempt_duration_seconds histogram
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.002"} 0
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.003"} 2
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.004"} 14
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.005"} 29
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.1"} 33
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="+Inf"} 34
grpc_client_attempt_duration_seconds_count{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 34
grpc_client_attempt_duration_seconds_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 0.46512665300000006
# HELP grpc_client_attempt_rcvd_total_compressed_message_size_bytes Compressed message bytes received per call attempt
# TYPE grpc_client_attempt_rcvd_total_compressed_message_size_bytes histogram
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.0"} 0
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 442.0
# HELP grpc_client_attempt_sent_total_compressed_message_size_bytes Compressed message bytes sent per client call attempt
# TYPE grpc_client_attempt_sent_total_compressed_message_size_bytes histogram
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.0"} 0
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="1024.0"} 34
grpc_client_attempt_sent_total_compressed_message_size_bytes_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 238.0
# HELP grpc_client_attempt_started_total Number of client call attempts started
# TYPE grpc_client_attempt_started_total counter
grpc_client_attempt_started_total{grpc_method="helloworld.Greeter/SayHello",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 34.0
# HELP grpc_client_call_duration_seconds Time taken by gRPC to complete an RPC from application's perspective
# TYPE grpc_client_call_duration_seconds histogram
grpc_client_call_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.0"} 0
grpc_client_call_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.003"} 2
grpc_client_call_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="+Inf"} 34
grpc_client_call_duration_seconds_count{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 34
grpc_client_call_duration_seconds_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 0.512708707
# TYPE target_info gauge
target_info{service_name="unknown_service:java",telemetry_sdk_language="java",telemetry_sdk_name="opentelemetry",telemetry_sdk_version="1.40.0"} 1

Allo stesso modo, per le metriche lato server -

curl localhost:9464/metrics

5. Visualizzazione delle metriche su Prometheus

Qui configureremo un'istanza Prometheus che eseguirà lo scraping del nostro client e server di esempio gRPC che esportano le metriche utilizzando Prometheus.

Scarica l'ultima release di Prometheus per la tua piattaforma, quindi estraila ed eseguila:

tar xvfz prometheus-*.tar.gz
cd prometheus-*

Crea un file di configurazione di Prometheus con quanto segue -

cat > grpc_otel_java_prometheus.yml <<EOF
scrape_configs:
  - job_name: "prometheus"
    scrape_interval: 5s
    static_configs:
      - targets: ["localhost:9090"]
  - job_name: "grpc-otel-java"
    scrape_interval: 5s
    static_configs:
      - targets: ["localhost:9464", "localhost:9465"]
EOF

Avvia Prometheus con la nuova configurazione -

./prometheus --config.file=grpc_otel_java_prometheus.yml

In questo modo, le metriche dei processi del codelab client e server verranno sottoposte a scraping ogni 5 secondi.

Vai a http://localhost:9090/graph per visualizzare le metriche. Ad esempio, la query -

histogram_quantile(0.5, rate(grpc_client_attempt_duration_seconds_bucket[1m]))

mostrerà un grafico con la latenza mediana dei tentativi utilizzando 1 minuto come finestra temporale per il calcolo dei quantili.

Frequenza delle query -

increase(grpc_client_attempt_duration_seconds_bucket[1m])

6. (Facoltativo) Esercizio per l'utente

Nelle dashboard di Prometheus, noterai che il QPS è basso. Prova a identificare del codice sospetto nell'esempio che limita il QPS.

Per gli appassionati, il codice client si limita ad avere una sola RPC in attesa in un determinato momento. Questo può essere modificato in modo che il client invii più RPC senza attendere il completamento delle precedenti. (La soluzione non è stata fornita.)