Einfaches OpenTelemetry-Plug-in in gRPC Java einrichten

1. Einführung

In diesem Codelab erstellen Sie mit gRPC einen Client und einen Server, die die Grundlage für eine in Java geschriebene Routenplanungsanwendung bilden.

Am Ende des Tutorials haben Sie eine einfache gRPC-HelloWorld-Anwendung, die mit dem gRPC-OpenTelemetry-Plug-in instrumentiert ist, und können die exportierten Beobachtbarkeitsmesswerte in Prometheus sehen.

Lerninhalte

  • OpenTelemetry-Plug-in für eine vorhandene gRPC-Java-Anwendung einrichten
  • Lokale Prometheus-Instanz ausführen
  • Messwerte nach Prometheus exportieren
  • Messwerte im Prometheus-Dashboard ansehen

2. Hinweis

Voraussetzungen

  • git
  • curl
  • JDK Version 8 oder höher

Installieren Sie die notwendigen Komponenten:

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

Code abrufen

Um Ihnen das Lernen zu erleichtern, bietet dieses Codelab ein vorgefertigtes Quellcode-Scaffold, mit dem Sie schnell loslegen können. In den folgenden Schritten erfahren Sie, wie Sie das gRPC-OpenTelemetry-Plug-in in einer Anwendung instrumentieren.

grpc-codelabs

Der Quellcode für das Scaffold für dieses Codelab ist in diesem GitHub- Verzeichnis verfügbar. Wenn Sie den Code nicht selbst implementieren möchten, finden Sie den vollständigen Quellcode im Verzeichnis „completed“.

Klonen Sie zuerst das gRPC-Codelab-Repository und wechseln Sie zum Ordner „grpc-java-opentelemetry“:

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

Alternativ können Sie die ZIP-Datei herunterladen, die nur das Codelab-Verzeichnis enthält, und sie manuell entzippen.

3. OpenTelemetry-Plug-in registrieren

Wir benötigen eine gRPC-Anwendung, um das gRPC-OpenTelemetry-Plug-in hinzuzufügen. In diesem Codelab verwenden wir einen einfachen gRPC-HelloWorld-Client und ‑Server, die wir mit dem gRPC-OpenTelemetry-Plug-in instrumentieren.

Im ersten Schritt registrieren Sie das OpenTelemetry-Plug-in, das mit einem Prometheus-Exporter konfiguriert ist, im Client. Öffnen Sie codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryClient.java mit Ihrem bevorzugten Editor und ändern Sie dann „main“, um Code zum Einrichten der gRPC-Java-OpenTelemetry-API hinzuzufügen.

Instrumentierung auf dem Client einrichten

Prometheus-Exporter erstellen

Erstellen Sie einen PrometheusHttpServer, um OpenTelemetry-Messwerte in das Prometheus-Format zu konvertieren und sie über einen HttpServer verfügbar zu machen. Mit dem folgenden Code-Snippet wird ein neuer Prometheus-Exporter erstellt.

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

OpenTelemetry-SDK-Instanz erstellen

Registrieren Sie den oben erstellten prometheusExporter als MetricReader, um Messwerte aus einem SdkMeterProvider zu lesen. SdkMeterProvider wird verwendet, um Messeinstellungen zu konfigurieren.

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

Erstellen Sie eine Instanz von OpenTelemetrySdk mit dem oben erstellten sdkMeterProvider für die SDK-Implementierung von OpenTelemetry.

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

GrpcOpenTelemetry-Instanz erstellen

Legen Sie mit der GrpcOpenTelemetry API das OpenTelemetry SDK fest, das den Prometheus-Messwertexporter verwendet.

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

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

Sobald eine GrpcOpenTelemetry-Instanz mit registerGlobal global registriert ist, werden alle nachfolgend erstellten gRPC-Clients und ‑Server mit OpenTelemetry instrumentiert.

OpenTelemetry SDK herunterfahren

Das Herunterfahren muss innerhalb des ShutDownHook erfolgen. openTelemetrySdk.close() fährt das SDK herunter und ruft auch das Herunterfahren für den SdkMeterProvider auf.

Instrumentierung auf dem Server einrichten

Fügen wir GrpcOpenTelemetry auch dem Server hinzu. Öffnen Sie codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryServer.java und fügen Sie Code hinzu, um GrpcOpenTelemetry zu initialisieren.

Prometheus-Exporter erstellen

Da dieses Codelab möglicherweise auf demselben Computer ausgeführt wird, verwenden wir einen anderen Port, um gRPC-Servermesswerte zu hosten, um Portkonflikte beim Erstellen von PrometheusHttpServer zu vermeiden.

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

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

OpenTelemetry-SDK-Instanz erstellen

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

GrpcOpenTelemetry mit OpenTelemetry SDK initialisieren

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

GrpcOpenTelemetry-Instanz erstellen

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

OpenTelemetry SDK herunterfahren

Nachdem der gRPC-Kanal heruntergefahren wurde. openTelemetrySdk.close() fährt das SDK herunter und ruft auch das Herunterfahren für den SdkMeterProvider auf.

4. Beispiel ausführen und Messwerte ansehen

Führen Sie Folgendes aus, um den Server auszuführen:

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

Bei einer erfolgreichen Einrichtung sehen Sie die folgende Ausgabe für den Server:

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

Während der Server ausgeführt wird, führen Sie den Client in einem anderen Terminal aus:

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

Eine erfolgreiche Ausführung sieht so aus:

[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

Wir haben das gRPC-OpenTelemetry-Plug-in so eingerichtet, dass Messwerte mit Prometheus exportiert werden. Diese Messwerte sind unter „localhost:9464“ für den Server und unter „localhost:9465“ für den Client verfügbar.

So sehen Sie Clientmesswerte:

curl localhost:9465/metrics

Das Ergebnis hat die folgende Form:

# 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

Entsprechend für die serverseitigen Messwerte:

curl localhost:9464/metrics

5. Messwerte in Prometheus ansehen

Hier richten wir eine Prometheus-Instanz ein, die unseren gRPC-Beispielclient und ‑server extrahiert, die Messwerte mit Prometheus exportieren.

Laden Sie die neueste Version von Prometheus für Ihre Plattform herunter, extrahieren Sie sie und führen Sie sie aus:

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

Erstellen Sie eine Prometheus-Konfigurationsdatei mit Folgendem:

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

Starten Sie Prometheus mit der neuen Konfiguration:

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

Dadurch werden die Messwerte aus den Codelab-Prozessen für Client und Server alle 5 Sekunden extrahiert.

Rufen Sie http://localhost:9090/graph auf, um die Messwerte anzusehen. Beispiel: Die Abfrage

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

zeigt ein Diagramm mit der medianen Latenz des Versuchs an, wobei 1 Minute als Zeitfenster für die Quantilberechnung verwendet wird.

Abfragerate:

increase(grpc_client_attempt_duration_seconds_bucket[1m])

6. (Optional) Übung für Nutzer

In den Prometheus-Dashboards sehen Sie, dass die QPS niedrig ist. Versuchen Sie, verdächtigen Code im Beispiel zu finden, der die QPS begrenzt.

Für Enthusiasten: Der Clientcode beschränkt sich darauf, zu einem bestimmten Zeitpunkt nur einen ausstehenden RPC zu haben. Dies kann so geändert werden, dass der Client mehr RPCs sendet, ohne auf den Abschluss der vorherigen zu warten. Die Lösung dafür wird nicht bereitgestellt.