إعداد المكوّن الإضافي الأساسي OpenTelemetry في gRPC Java

1. مقدمة

في هذا الدرس التطبيقي حول الترميز، ستستخدم gRPC لإنشاء عميل وخادم يشكّلان الأساس لتطبيق يحدّد المسارات مكتوب بلغة Java.

في نهاية هذا البرنامج التعليمي، سيكون لديك تطبيق gRPC HelloWorld بسيط مزوّد بالمكوّن الإضافي gRPC OpenTelemetry، وستتمكّن من الاطّلاع على مقاييس قابلية المراقبة التي تم تصديرها في Prometheus.

أهداف الدورة التعليمية

  • كيفية إعداد المكوّن الإضافي OpenTelemetry لتطبيق gRPC Java حالي
  • تشغيل نسخة محلية من Prometheus
  • تصدير المقاييس إلى Prometheus
  • عرض المقاييس من لوحة بيانات Prometheus

2. قبل البدء

المتطلبات

  • git
  • curl
  • الإصدار JDK v8 أو الإصدارات الأحدث

ثبِّت المتطلبات الأساسية:

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

الحصول على الشفرة‏

لتبسيط عملية التعلّم، يوفّر لك هذا الدرس التطبيقي حول الترميز بنية أساسية مُعدّة مسبقًا لرمز المصدر لمساعدتك على البدء. ستوضّح لك الخطوات التالية كيفية إعداد إضافة gRPC OpenTelemetry في أحد التطبيقات.

grpc-codelabs

يتوفّر رمز المصدر الخاص بهذا الدرس التطبيقي حول الترميز في دليل github هذا. إذا كنت تفضّل عدم تنفيذ الرمز بنفسك، يتوفّر الرمز المصدر المكتمل في الدليل المكتمل.

أولاً، استنسِخ مستودع grpc codelab وانتقِل إلى المجلد grpc-java-opentelemetry:

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

بدلاً من ذلك، يمكنك تنزيل ملف ‎ .zip الذي يحتوي على دليل الدرس العملي فقط وفك ضغطه يدويًا.

3- تسجيل OpenTelemetry Plugin

نحتاج إلى تطبيق gRPC لإضافة المكوّن الإضافي gRPC OpenTelemetry. في هذا الدرس التطبيقي حول الترميز، سنستخدم عميل وخادم بسيطَين من gRPC HelloWorld، وسنجهّزهما بالمكوّن الإضافي gRPC OpenTelemetry.

خطوتك الأولى هي تسجيل إضافة OpenTelemetry التي تم ضبطها باستخدام أداة تصدير Prometheus في العميل. افتح codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryClient.java باستخدام المحرّر المفضّل لديك، ثم عدِّل main لإضافة رمز لإعداد gRPC Java OpenTelemetry API.

إعداد أدوات القياس على العميل

إنشاء أداة تصدير Prometheus

أنشئ PrometheusHttpServer لتحويل مقاييس OpenTelemetry إلى تنسيق Prometheus وعرضها من خلال HttpServer. ينشئ مقتطف الرمز البرمجي التالي Prometheus Exporter جديدًا.

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

إنشاء مثيل OpenTelemetry SDK

تسجيل prometheusExporter أعلاه كـ MetricReader لقراءة المقاييس من SdkMeterProvider يتم استخدام SdkMeterProvider لضبط إعدادات المقاييس.

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

أنشئ مثيلاً من OpenTelemetrySdk باستخدام sdkMeterProvider الذي تم إنشاؤه أعلاه لتنفيذ حزمة تطوير البرامج (SDK) الخاصة بـ OpenTelemetry.

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

إنشاء مثيل GrpcOpenTelemetry

باستخدام واجهة برمجة التطبيقات GrpcOpenTelemetry API، اضبط حزمة تطوير البرامج (SDK) الخاصة بـ OpenTelemetry التي تستخدم أداة تصدير مقاييس Prometheus.

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

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

بعد تسجيل مثيل GrpcOpenTelemetry على مستوى العالم باستخدام registerGlobal، سيتم تزويد جميع عملاء وخوادم gRPC التي تم إنشاؤها بعد ذلك بأدوات OpenTelemetry.

إيقاف حزمة تطوير البرامج (SDK) الخاصة بمنصة OpenTelemetry

يجب أن يتم الإيقاف داخل ShutDownHook. تؤدي الدالة openTelemetrySdk.close() إلى إيقاف حزمة SDK، كما تستدعي الدالة shutdown في SdkMeterProvider.

إعداد أدوات القياس على الخادم

وبالمثل، لنضِف GrpcOpenTelemetry إلى الخادم أيضًا. افتح codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryServer.java وأضِف الرمز البرمجي لبدء GrpcOpenTelemetry.

إنشاء أداة تصدير Prometheus

بما أنّ هذا الدرس العملي قد يتم تنفيذه من الجهاز نفسه، سنستخدم منفذًا مختلفًا لاستضافة مقاييس جهة خادم gRPC لتجنُّب تعارض المنافذ أثناء إنشاء PrometheusHttpServer.

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

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

إنشاء مثيل OpenTelemetry SDK

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

إعداد GrpcOpenTelemetry باستخدام حزمة تطوير البرامج (SDK) الخاصة بـ OpenTelemetry

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

إنشاء مثيل GrpcOpenTelemetry

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

إيقاف حزمة تطوير البرامج (SDK) الخاصة بمنصة OpenTelemetry

بعد إيقاف قناة gRPC يؤدي استدعاء openTelemetrySdk.close() إلى إيقاف حزمة SDK وإيقاف SdkMeterProvider أيضًا.

4. تشغيل المثال وعرض المقاييس

لتشغيل الخادم، نفِّذ الأمر التالي:

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

بعد إعداد الخادم بنجاح، ستظهر لك النتيجة التالية:

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

أثناء تشغيل الخادم، شغِّل العميل في وحدة طرفية أخرى:

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

سيبدو التنفيذ الناجح على النحو التالي:

[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

بما أنّنا أعددنا المكوّن الإضافي gRPC OpenTelemetry لتصدير المقاييس باستخدام Prometheus. ستتوفّر هذه المقاييس على localhost:9464 للخادم وlocalhost:9465 للعميل.

للاطّلاع على مقاييس العميل، اتّبِع الخطوات التالية:

curl localhost:9465/metrics

ستكون النتيجة على الشكل التالي:

# 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

وبالمثل، بالنسبة إلى مقاييس جهة الخادم:

curl localhost:9464/metrics

5- عرض المقاييس على Prometheus

سنعمل هنا على إعداد مثيل Prometheus الذي سيزحف إلى مثال عميل وخادم gRPC اللذين يصدّران المقاييس باستخدام Prometheus.

نزِّل أحدث إصدار من Prometheus لنظام التشغيل الخاص بك، ثم استخرِجه وشغِّله:

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

أنشئ ملف إعداد Prometheus باستخدام ما يلي:

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

ابدأ prometheus بالإعدادات الجديدة -

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

سيؤدي ذلك إلى ضبط مقاييس من عمليات codelab الخاصة بالعميل والخادم ليتم جمعها كل 5 ثوانٍ.

انتقِل إلى http://localhost:9090/graph لعرض المقاييس. على سبيل المثال، طلب البحث -

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

سيعرض الرسم البياني متوسط وقت استجابة المحاولة باستخدام دقيقة واحدة كإطار زمني لاحتساب الكمية.

معدّل طلبات البحث -

increase(grpc_client_attempt_duration_seconds_bucket[1m])

6. (اختياري) تمرين للمستخدم

في لوحات بيانات Prometheus، ستلاحظ أنّ عدد الطلبات في الثانية منخفض. تحقَّق ممّا إذا كان بإمكانك تحديد بعض الرموز المشبوهة في المثال التي تحدّ من عدد الطلبات في الثانية.

بالنسبة إلى المستخدمين المتحمّسين، يقتصر رمز العميل على طلب إجراء مكالمة إجراء عن بُعد واحدة فقط في لحظة معيّنة. يمكن تعديل ذلك ليتمكّن العميل من إرسال المزيد من طلبات RPC بدون انتظار اكتمال الطلبات السابقة. (لم يتم تقديم الحلّ لهذه المشكلة).