Einfaches OpenTelemetry-Plug-in in gRPC Java einrichten

1. Einführung

In diesem Codelab verwenden Sie gRPC, um einen Client und einen Server zu erstellen, die die Grundlage einer in Java geschriebenen Anwendung zur Routenplanung 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 v8 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 den Einstieg zu erleichtern, wird in diesem Codelab ein vorgefertigtes Quellcode-Gerüst bereitgestellt. In den folgenden Schritten wird beschrieben, wie Sie das gRPC OpenTelemetry-Plug-in in einer Anwendung instrumentieren.

grpc-codelabs

Der Gerüstquellcode 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 in den Ordner „grpc-java-opentelemetry“:

git clone 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 entpacken.

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.

Als Erstes müssen Sie das OpenTelemetry-Plug-in, das mit einem Prometheus-Exporter konfiguriert ist, im Client registrieren. Öffnen Sie codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryClient.java mit Ihrem bevorzugten Editor und fügen Sie dann Code hinzu, um die gRPC Java OpenTelemetry API einzurichten.

Instrumentierung auf dem Client einrichten

Prometheus-Exporter erstellen

Erstellen Sie einen PrometheusHttpServer, um OpenTelemetry-Messwerte in das Prometheus-Format zu konvertieren und über einen HttpServer bereitzustellen. 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 oben prometheusExporter als MetricReader, um Messwerte aus einem SdkMeterProvider zu lesen. SdkMeterProvider wird verwendet, um Metrikeinstellungen 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-Messwerte-Exporter verwendet.

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

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

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

OpenTelemetry SDK herunterfahren

Das Herunterfahren muss im ShutDownHook erfolgen. openTelemetrySdk.close() beendet das SDK und ruft auch „shutdown“ 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 für die serverseitigen gRPC-Messwerte, 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 dem 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 geschlossen wurde. Durch Aufrufen von openTelemetrySdk.close() wird das SDK heruntergefahren und auch „shutdown“ für den SdkMeterProvider aufgerufen.

4. Beispiel ausführen und Messwerte ansehen

So führen Sie den Server aus:

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

Bei einer erfolgreichen Einrichtung wird für den Server die folgende Ausgabe angezeigt:

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

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

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

Ein erfolgreicher Lauf 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 auf localhost:9464 für den Server und auf localhost:9465 für den Client verfügbar.

So rufen Sie Clientmesswerte auf:

curl localhost:9465/metrics

Das Ergebnis würde so aussehen:

# 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

Ähnlich verhält es sich bei den serverseitigen Messwerten:

curl localhost:9464/metrics

5. Messwerte in Prometheus ansehen

Hier richten wir eine Prometheus-Instanz ein, die unseren gRPC-Beispielclient und -Server abruft, die Messwerte mit Prometheus exportieren.

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

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

Erstellen Sie eine Prometheus-Konfigurationsdatei mit den folgenden Inhalten:

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 der Client- und Server-Codelab-Prozesse alle 5 Sekunden erfasst.

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

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

wird ein Diagramm mit der medianen Latenz für Versuche angezeigt, wobei für die Quantilberechnung ein Zeitfenster von einer Minute verwendet wird.

Anzahl der Anfragen –

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. Sehen Sie nach, ob Sie im Beispielcode verdächtigen Code finden, der die QPS begrenzt.

Für den Enthusiasten beschränkt sich der Clientcode 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 hierfür wurde nicht bereitgestellt.)