Configurer le plug-in OpenTelemetry de base dans gRPC Java

1. Introduction

Dans cet atelier de programmation, vous allez utiliser gRPC pour créer un client et un serveur qui constituent la base d'une application de cartographie d'itinéraire écrite en Java.

À la fin de ce tutoriel, vous disposerez d'une application gRPC HelloWorld simple instrumentée avec le plug-in gRPC OpenTelemetry. Vous pourrez également consulter les métriques d'observabilité exportées dans Prometheus.

Points abordés

  • Configurer le plug-in OpenTelemetry pour une application Java gRPC existante
  • Exécuter une instance Prometheus locale
  • Exporter des métriques vers Prometheus
  • Afficher les métriques du tableau de bord Prometheus

2. Avant de commencer

Prérequis

  • git
  • curl
  • JDK v8 ou version ultérieure

Installez les prérequis :

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

Obtenir le code

Pour faciliter votre apprentissage, cet atelier de programmation propose une structure de code source prédéfinie pour vous aider à démarrer. Les étapes suivantes vous guideront dans l'instrumentation du plug-in gRPC OpenTelemetry dans une application.

grpc-codelabs

Le code source du canevas pour cet atelier de programmation est disponible dans ce répertoire GitHub. Si vous préférez ne pas implémenter le code vous-même, le code source complet est disponible dans le répertoire completed.

Tout d'abord, clonez le dépôt de l'atelier de programmation gRPC et accédez au dossier grpc-java-opentelemetry :

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

Vous pouvez également télécharger le fichier .zip contenant uniquement le répertoire de l'atelier de programmation et le décompresser manuellement.

3. Enregistrer le plug-in OpenTelemetry

Nous avons besoin d'une application gRPC pour ajouter le plug-in gRPC OpenTelemetry. Dans cet atelier de programmation, nous allons utiliser un client et un serveur HelloWorld gRPC simples que nous instrumenterons avec le plug-in OpenTelemetry gRPC.

La première étape consiste à enregistrer le plug-in OpenTelemetry configuré avec un exportateur Prometheus dans le client. Ouvrez codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryClient.java avec votre éditeur préféré, puis modifiez la fonction principale pour ajouter du code afin de configurer l'API gRPC Java OpenTelemetry.

Configurer l'instrumentation sur le client

Créer un exportateur Prometheus

Créez un PrometheusHttpServer pour convertir les métriques OpenTelemetry au format Prometheus et les exposer via un HttpServer. L'extrait de code suivant crée un exportateur Prometheus.

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

Créer une instance du SDK OpenTelemetry

Enregistrez prometheusExporter ci-dessus en tant que MetricReader pour lire les métriques d'un SdkMeterProvider. SdkMeterProvider permet de configurer les paramètres des métriques.

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

Créez une instance d'OpenTelemetrySdk avec le sdkMeterProvider créé ci-dessus pour l'implémentation du SDK OpenTelemetry.

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

Créer une instance GrpcOpenTelemetry

À l'aide de l'API GrpcOpenTelemetry, définissez le SDK OpenTelemetry qui utilise l'exportateur de métriques Prometheus.

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

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

Une fois qu'une instance GrpcOpenTelemetry est enregistrée globalement à l'aide de registerGlobal, tous les clients et serveurs gRPC créés par la suite seront instrumentés avec OpenTelemetry.

Arrêter le SDK OpenTelemetry

L'arrêt doit avoir lieu dans ShutDownHook. openTelemetrySdk.close() arrête le SDK et appelle également l'arrêt sur SdkMeterProvider.

Configurer l'instrumentation sur le serveur

De même, ajoutons GrpcOpenTelemetry au serveur. Ouvrez codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryServer.java et ajoutez du code pour initialiser GrpcOpenTelemetry.

Créer un exportateur Prometheus

Étant donné que cet atelier de programmation peut être exécuté à partir de la même machine, nous utilisons un port différent pour héberger les métriques côté serveur gRPC afin d'éviter les conflits de ports lors de la création de PrometheusHttpServer.

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

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

Créer une instance du SDK OpenTelemetry

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

Initialiser GrpcOpenTelemetry avec le SDK OpenTelemetry

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

Créer une instance GrpcOpenTelemetry

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

Arrêter le SDK OpenTelemetry

Après l'arrêt du canal gRPC. L'appel de openTelemetrySdk.close() arrête le SDK et appelle également l'arrêt sur SdkMeterProvider.

4. Exécuter l'exemple et afficher les métriques

Pour exécuter le serveur, exécutez la commande suivante :

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

Si la configuration réussit, le résultat suivant s'affiche pour le serveur :

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

Pendant que le serveur est en cours d'exécution, exécutez le client sur un autre terminal :

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

Une exécution réussie se présente comme suit :

[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

Étant donné que nous avons configuré le plug-in gRPC OpenTelemetry pour exporter les métriques à l'aide de Prometheus. Ces métriques seront disponibles sur localhost:9464 pour le serveur et sur localhost:9465 pour le client.

Pour afficher les métriques client :

curl localhost:9465/metrics

Le résultat doit respecter le format suivant :

# 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

De même, pour les métriques côté serveur :

curl localhost:9464/metrics

5. Afficher les métriques sur Prometheus

Ici, nous allons configurer une instance Prometheus qui extraira notre exemple de client et de serveur gRPC exportant des métriques à l'aide de Prometheus.

Téléchargez la dernière version de Prometheus pour votre plate-forme, puis extrayez-la et exécutez-la :

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

Créez un fichier de configuration Prometheus avec les éléments suivants :

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

Démarrez Prometheus avec la nouvelle configuration :

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

Cela configurera les métriques des processus de l'atelier de programmation client et serveur pour qu'elles soient récupérées toutes les cinq secondes.

Accédez à http://localhost:9090/graph pour afficher les métriques. Par exemple, la requête

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

affichera un graphique avec la latence médiane des tentatives, en utilisant une fenêtre temporelle d'une minute pour le calcul du quantile.

Taux de requêtes :

increase(grpc_client_attempt_duration_seconds_bucket[1m])

6. (Facultatif) Exercice pour l'utilisateur

Dans les tableaux de bord Prometheus, vous remarquerez que le QPS est faible. Essayez d'identifier dans l'exemple de code un code suspect qui limite les requêtes par seconde.

Pour les enthousiastes, le code client se limite à n'avoir qu'un seul RPC en attente à un moment donné. Vous pouvez modifier cette valeur pour que le client envoie davantage de RPC sans attendre que les précédents se terminent. (La solution n'a pas été fournie.)