1. Introdução
Neste codelab, você vai usar o gRPC para criar um cliente e um servidor que formam a base de um aplicativo de mapeamento de rotas escrito em Java.
Ao final do tutorial, você terá um aplicativo gRPC HelloWorld simples instrumentado com o plug-in gRPC OpenTelemetry e poderá conferir as métricas de observabilidade exportadas no Prometheus.
O que você vai aprender
- Como configurar o plug-in OpenTelemetry para um aplicativo gRPC Java
- Como executar uma instância local do Prometheus
- Como exportar métricas para o Prometheus
- Como conferir métricas no painel do Prometheus
2. Antes de começar
O que é necessário
gitcurlJDKv8 ou mais recente
Instale os pré-requisitos:
sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git curl
Acessar o código
Para simplificar o aprendizado, este codelab oferece um scaffold de código-fonte pré-criado para ajudar você a começar. As etapas a seguir orientam você na instrumentação do plug-in gRPC OpenTelemetry em um aplicativo.
O código-fonte do scaffold para este codelab está disponível neste diretório do GitHub . Se você preferir não implementar o código, o código-fonte concluído está disponível no diretório completed.
Primeiro, clone o repositório do codelab do gRPC e acesse a pasta grpc-java-opentelemetry:
git clone -b v1 https://github.com/grpc-ecosystem/grpc-codelabs.git
cd grpc-codelabs/codelabs/grpc-java-opentelemetry/
Como alternativa, você pode baixar o arquivo .zip que contém apenas o diretório do codelab e descompactá-lo manualmente.
3. Registrar o plug-in OpenTelemetry
Precisamos de um aplicativo gRPC para adicionar o plug-in gRPC OpenTelemetry. Neste codelab, vamos usar um cliente e um servidor gRPC HelloWorld simples que vamos instrumentar com o plug-in gRPC OpenTelemetry.
A primeira etapa é registrar o plug-in OpenTelemetry configurado com um exportador do Prometheus no cliente. Abra codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryClient.java com seu editor favorito e modifique o principal para adicionar código para configurar a API gRPC Java OpenTelemetry.
Configurar a instrumentação no cliente
Criar exportador do Prometheus
Crie um PrometheusHttpServer para converter métricas do OpenTelemetry para o formato do Prometheus e exponha-as por um HttpServer. O snippet de código a seguir cria um novo exportador do Prometheus.
// Default prometheus port i.e `prometheusPort` has been initialized to 9465
PrometheusHttpServer prometheusExporter = PrometheusHttpServer.builder()
.setPort(prometheusPort)
.build();
Criar instância do SDK do OpenTelemetry
Registre o prometheusExporter criado acima como MetricReader para ler métricas de um SdkMeterProvider. O SdkMeterProvider é usado para configurar as configurações de métricas.
SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
.registerMetricReader(prometheusExporter)
.build();
Crie uma instância do OpenTelemetrySdk com o sdkMeterProvider criado acima para a implementação do SDK do OpenTelemetry.
OpenTelemetrySdk openTelemetrySdk =OpenTelemetrySdk.builder()
.setMeterProvider(sdkMeterProvider)
.build();
Criar instância do GrpcOpenTelemetry
Usando a API GrpcOpenTelemetry, defina o SDK do OpenTelemetry que usa o exportador de métricas do Prometheus.
GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
.sdk(openTelemetrySdk)
.build();
// Registers gRPC OpenTelemetry globally.
grpcOpenTelmetry.registerGlobal();
Depois que uma instância do GrpcOpenTelemetry for registrada globalmente usando registerGlobal, todos os clientes e servidores gRPC criados posteriormente serão instrumentados com o OpenTelemetry.
Desativar o SDK do OpenTelemetry
A desativação precisa ocorrer dentro do ShutDownHook. openTelemetrySdk.close() desativa o SDK e também chama a desativação no SdkMeterProvider.
Configurar a instrumentação no servidor
Da mesma forma, vamos adicionar o GrpcOpenTelemetry ao servidor. Abra codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryServer.java e adicione código para inicializar o GrpcOpenTelemetry.
Criar exportador do Prometheus
Como este codelab pode ser executado na mesma máquina, estamos usando uma porta diferente para hospedar métricas do lado do servidor gRPC para evitar conflitos de porta ao criar o PrometheusHttpServer.
// Default prometheus port i.e `prometheusPort` has been set to 9464
PrometheusHttpServer prometheusExporter = PrometheusHttpServer.builder()
.setPort(prometheusPort)
.build();
Criar instância do SDK do OpenTelemetry
SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
.registerMetricReader(prometheusExporter)
.build();
Inicializar o GrpcOpenTelemetry com o SDK do OpenTelemetry
OpenTelemetrySdk openTelemetrySdk =OpenTelemetrySdk.builder()
.setMeterProvider(sdkMeterProvider)
.build();
Criar instância do GrpcOpenTelemetry
GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
.sdk(openTelemetrySdk)
.build();
// Registers gRPC OpenTelemetry globally.
grpcOpenTelmetry.registerGlobal();
Desativar o SDK do OpenTelemetry
Depois que o canal gRPC for desativado. Chamar openTelemetrySdk.close() desativa o SDK e também chama a desativação no SdkMeterProvider.
4. Executar o exemplo e conferir métricas
Para executar o servidor, execute -
cd start_here
../gradlew installDist
./build/install/start_here/bin/opentelemetry-server
Com uma configuração bem-sucedida, você verá a seguinte saída para o servidor -
[date and time] io.grpc.codelabs.opentelemetry.OpenTelemetryServer start
INFO: Server started, listening on 50051
Enquanto o servidor estiver em execução, em outro terminal, execute o cliente -
./build/install/start_here/bin/opentelemetry-client world
Uma execução bem-sucedida será assim -
[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
Como configuramos o plug-in gRPC OpenTelemetry para exportar métricas usando o Prometheus. Essas métricas estarão disponíveis em localhost:9464 para o servidor e localhost:9465 para o cliente.
Para conferir as métricas do cliente -
curl localhost:9465/metrics
O resultado será do formulário -
# 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
Da mesma forma, para as métricas do lado do servidor -
curl localhost:9464/metrics
5. Conferir métricas no Prometheus
Aqui, vamos configurar uma instância do Prometheus que vai coletar nosso cliente e servidor de exemplo do gRPC que estão exportando métricas usando o Prometheus.
Faça o download da versão mais recente do Prometheus para sua plataforma, extraia e execute:
tar xvfz prometheus-*.tar.gz
cd prometheus-*
Crie um arquivo de configuração do Prometheus com o seguinte -
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
Inicie o Prometheus com a nova configuração -
./prometheus --config.file=grpc_otel_java_prometheus.yml
Isso vai configurar as métricas dos processos do codelab do cliente e do servidor para serem coletadas a cada 5 segundos.
Acesse http://localhost:9090/graph para conferir as métricas. Por exemplo, a consulta -
histogram_quantile(0.5, rate(grpc_client_attempt_duration_seconds_bucket[1m]))
vai mostrar um gráfico com a latência mediana da tentativa usando 1 minuto como janela de tempo para o cálculo do quantil.
Taxa de consultas -
increase(grpc_client_attempt_duration_seconds_bucket[1m])
6. (Opcional) Exercício para o usuário
Nos painéis do Prometheus, você vai notar que o QPS é baixo. Confira se você consegue identificar algum código suspeito no exemplo que está limitando o QPS.
Para os entusiastas, o código do cliente se limita a ter apenas um RPC pendente em um determinado momento. Isso pode ser modificado para que o cliente envie mais RPCs sem esperar que os anteriores sejam concluídos. A solução para isso não foi fornecida.