1. Wprowadzenie
W tym ćwiczeniu dowiesz się, jak za pomocą gRPC utworzyć klienta i serwer, które będą stanowić podstawę aplikacji do mapowania tras napisanej w Javie.
Po ukończeniu tego samouczka będziesz mieć prostą aplikację gRPC HelloWorld instrumentowaną za pomocą wtyczki gRPC OpenTelemetry i będziesz w stanie wyświetlać wyeksportowane wskaźniki dostrzegalności w Prometheusie.
Czego się nauczysz
- Konfigurowanie wtyczki OpenTelemetry w istniejącej aplikacji gRPC Java
- Uruchamianie lokalnej instancji Prometheusa
- Eksportowanie wskaźników do Prometheusa
- Wyświetlanie danych z panelu Prometheusa
2. Zanim zaczniesz
Czego potrzebujesz
gitcurlJDKw wersji 8 lub nowszej
Zainstaluj wstępnie wymagane usługi:
sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git curl
Pobierz kod
W tym ćwiczeniu znajdziesz gotowy szkielet kodu źródłowego, który pomoże Ci zacząć i w ten sposób ułatwi Ci naukę. Z podanych niżej instrukcji dowiesz się, jak zintegrować wtyczkę gRPC OpenTelemetry z aplikacją.
Kod źródłowy szkieletu tego ćwiczenia jest dostępny w tym katalogu na GitHubie. Jeśli nie chcesz samodzielnie implementować kodu, gotowy kod źródłowy znajdziesz w katalogu completed.
Najpierw sklonuj repozytorium z ćwiczeniami z gRPC i przejdź do folderu grpc-java-opentelemetry:
git clone https://github.com/grpc-ecosystem/grpc-codelabs.git
cd grpc-codelabs/codelabs/grpc-java-opentelemetry/
Możesz też pobrać plik ZIP zawierający tylko katalog z ćwiczeniem i rozpakować go ręcznie.
3. Rejestrowanie wtyczki OpenTelemetry
Aby dodać wtyczkę gRPC OpenTelemetry, potrzebujemy aplikacji gRPC. W tym ćwiczeniu użyjemy prostego klienta i serwera gRPC HelloWorld, które wyposażymy we wtyczkę gRPC OpenTelemetry.
Najpierw zarejestruj w kliencie wtyczkę OpenTelemetry skonfigurowaną z eksporterem Prometheusa. Otwórz plik codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryClient.java w ulubionym edytorze, a następnie zmodyfikuj główny plik, aby dodać kod konfigurujący interfejs gRPC Java OpenTelemetry API.
Konfigurowanie instrumentacji po stronie klienta
Tworzenie eksportera Prometheusa
Utwórz obiekt PrometheusHttpServer, aby przekonwertować wskaźniki OpenTelemetry na format Prometheus i udostępnić je za pomocą obiektu HttpServer. Poniższy fragment kodu tworzy nowy eksporter Prometheusa.
// Default prometheus port i.e `prometheusPort` has been initialized to 9465
PrometheusHttpServer prometheusExporter = PrometheusHttpServer.builder()
.setPort(prometheusPort)
.build();
Tworzenie instancji pakietu OpenTelemetry SDK
Zarejestruj powyższy obiekt prometheusExporter jako MetricReader, aby odczytywać dane z obiektu SdkMeterProvider. Do konfigurowania ustawień danych służy klasa SdkMeterProvider.
SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
.registerMetricReader(prometheusExporter)
.build();
Utwórz instancję OpenTelemetrySdk z utworzonym powyżej sdkMeterProvider na potrzeby implementacji pakietu SDK OpenTelemetry.
OpenTelemetrySdk openTelemetrySdk =OpenTelemetrySdk.builder()
.setMeterProvider(sdkMeterProvider)
.build();
Tworzenie instancji GrpcOpenTelemetry
Za pomocą interfejsu API GrpcOpenTelemetry ustaw pakiet SDK OpenTelemetry, który używa eksportera wskaźników Prometheusa.
GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
.sdk(openTelemetrySdk)
.build();
// Registers gRPC OpenTelemetry globally.
grpcOpenTelmetry.registerGlobal();
Gdy instancja GrpcOpenTelemetry zostanie zarejestrowana globalnie za pomocą registerGlobal, wszystkie utworzone później klienty i serwery gRPC będą instrumentowane za pomocą OpenTelemetry.
Wyłączanie pakietu OpenTelemetry SDK
Zamykanie musi odbywać się w ramach ShutDownHook. openTelemetrySdk.close() zamyka pakiet SDK i wywołuje też zamknięcie w przypadku SdkMeterProvider.
Konfigurowanie instrumentacji na serwerze
Podobnie dodajmy GrpcOpenTelemetry do serwera. Otwórz plik codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryServer.java i dodaj kod inicjujący GrpcOpenTelemetry.
Tworzenie eksportera Prometheusa
Ponieważ to ćwiczenie może być wykonywane na tym samym komputerze, używamy innego portu do hostowania wskaźników po stronie serwera gRPC, aby uniknąć konfliktów portów podczas tworzenia PrometheusHttpServer.
// Default prometheus port i.e `prometheusPort` has been set to 9464
PrometheusHttpServer prometheusExporter = PrometheusHttpServer.builder()
.setPort(prometheusPort)
.build();
Tworzenie instancji pakietu OpenTelemetry SDK
SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
.registerMetricReader(prometheusExporter)
.build();
Inicjowanie GrpcOpenTelemetry za pomocą pakietu OpenTelemetry SDK
OpenTelemetrySdk openTelemetrySdk =OpenTelemetrySdk.builder()
.setMeterProvider(sdkMeterProvider)
.build();
Tworzenie instancji GrpcOpenTelemetry
GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
.sdk(openTelemetrySdk)
.build();
// Registers gRPC OpenTelemetry globally.
grpcOpenTelmetry.registerGlobal();
Wyłączanie pakietu OpenTelemetry SDK
Po zamknięciu kanału gRPC. Wywołanie openTelemetrySdk.close() zamyka pakiet SDK i wywołuje też zamknięcie w przypadku SdkMeterProvider.
4. Uruchamianie przykładu i wyświetlanie danych
Aby uruchomić serwer, wpisz:
cd start_here
../gradlew installDist
./build/install/start_here/bin/opentelemetry-server
Po pomyślnej konfiguracji na serwerze zobaczysz te dane wyjściowe:
[date and time] io.grpc.codelabs.opentelemetry.OpenTelemetryServer start
INFO: Server started, listening on 50051
Gdy serwer działa, w innym terminalu uruchom klienta:
./build/install/start_here/bin/opentelemetry-client world
Prawidłowe uruchomienie będzie wyglądać tak:
[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
Skonfigurowaliśmy wtyczkę gRPC OpenTelemetry do eksportowania wskaźników za pomocą Prometheusa. Te dane będą dostępne na serwerze localhost:9464 i na kliencie localhost:9465.
Aby wyświetlić dane klienta:
curl localhost:9465/metrics
Wynik będzie miał postać:
# 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
Podobnie w przypadku danych po stronie serwera:
curl localhost:9464/metrics
5. Wyświetlanie danych w usłudze Prometheus
Skonfigurujemy tu instancję Prometheusa, która będzie pobierać dane z naszego przykładowego klienta i serwera gRPC eksportujących wskaźniki za pomocą Prometheusa.
Pobierz najnowszą wersję Prometheusa na swoją platformę, a następnie rozpakuj ją i uruchom:
tar xvfz prometheus-*.tar.gz
cd prometheus-*
Utwórz plik konfiguracji usługi Prometheus o tej treści:
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
Uruchom usługę Prometheus z nową konfiguracją:
./prometheus --config.file=grpc_otel_java_prometheus.yml
Spowoduje to skonfigurowanie wskaźników klienta i serwera z procesów zainicjowanych w ćwiczeniu tak, aby były pobierane co 5 sekund.
Aby wyświetlić wartości wskaźników, otwórz stronę http://localhost:9090/graph. Na przykład zapytanie
histogram_quantile(0.5, rate(grpc_client_attempt_duration_seconds_bucket[1m]))
wyświetli wykres z medianą czasu oczekiwania na próby z 1-minutowym oknem czasowym do obliczania kwantyla.
Częstotliwość zapytań:
increase(grpc_client_attempt_duration_seconds_bucket[1m])
6. (Opcjonalnie) Ćwiczenie dla użytkownika
W panelach Prometheusa zauważysz, że ilość zapytań/s jest niska. Sprawdź, czy w przykładzie możesz znaleźć podejrzany kod, który ogranicza liczbę zapytań na sekundę.
Uwaga dla entuzjastów: kod klienta ogranicza się do jednego oczekującego wywołania RPC w danym momencie. Można to zmodyfikować tak, aby klient wysyłał więcej wywołań RPC bez czekania na zakończenie poprzednich. (Rozwiązanie tego problemu nie zostało podane).