راه اندازی افزونه پایه OpenTelemetry در gRPC C

۱. مقدمه

در این آزمایشگاه کد، شما از gRPC برای ایجاد یک کلاینت و سرور استفاده خواهید کرد که پایه و اساس یک برنامه مسیریابی نوشته شده با زبان ++C را تشکیل می‌دهند.

در پایان این آموزش، شما یک برنامه ساده gRPC HelloWorld مجهز به افزونه gRPC OpenTelemetry خواهید داشت و می‌توانید معیارهای مشاهده‌پذیری خروجی گرفته شده را در Prometheus مشاهده کنید.

آنچه یاد خواهید گرفت

  • نحوه تنظیم افزونه OpenTelemetry برای برنامه gRPC C++ موجود
  • اجرای یک نمونه محلی پرومتئوس
  • خروجی گرفتن از معیارها به پرومتئوس
  • مشاهده معیارها از داشبورد پرومتئوس

۲. قبل از شروع

آنچه نیاز دارید

  • git
  • curl
  • build-essential
  • clang
  • bazel برای ساخت مثال در این آزمایشگاه کد

پیش‌نیازها را نصب کنید:

sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git curl build-essential clang

bazel می‌توان از طریق bazelisk نصب کرد. آخرین نسخه را می‌توانید اینجا پیدا کنید.

یک راه ساده برای تنظیم آن، نصب آن به عنوان فایل باینری bazel در PATH به صورت زیر است:

sudo cp bazelisk-linux-amd64 /usr/local/bin/bazel
sudo chmod a+x /usr/local/bin/bazel

به عنوان یک روش جایگزین، می‌توانید از CMake نیز استفاده کنید. دستورالعمل‌های استفاده از CMake را می‌توانید اینجا بیابید.

کد را دریافت کنید

برای ساده‌سازی یادگیری شما، این codelab یک چارچوب کد منبع از پیش ساخته شده ارائه می‌دهد تا به شما در شروع کار کمک کند. مراحل زیر شما را در استفاده از افزونه gRPC OpenTelemetry در یک برنامه راهنمایی می‌کند.

grpc-codelabs

کد منبع scaffold برای این codelab در این دایرکتوری github موجود است. اگر ترجیح می‌دهید کد را خودتان پیاده‌سازی نکنید، کد منبع تکمیل‌شده در دایرکتوری completed موجود است.

ابتدا، مخزن grpc codelab را کلون کنید و با دستور cd به پوشه grpc-cpp-opentelemetry بروید:

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

روش دیگر این است که فایل .zip که فقط شامل دایرکتوری codelab است را دانلود کرده و به صورت دستی آن را از حالت فشرده خارج کنید.

کتابخانه gRPC را با استفاده از bazel بسازید:

bazel build start_here/...

۳. افزونه OpenTelemetry را ثبت کنید

برای اضافه کردن افزونه gRPC OpenTelemetry به یک برنامه gRPC نیاز داریم. در این آزمایشگاه کد، از یک کلاینت و سرور ساده gRPC HelloWorld استفاده خواهیم کرد که آن را با افزونه gRPC OpenTelemetry تجهیز خواهیم کرد.

اولین قدم شما ثبت افزونه OpenTelemetry پیکربندی شده با یک صادرکننده Prometheus در کلاینت است. codelabs/grpc-cpp-opentelemetry/start_here/greeter_callback_client.cc با ویرایشگر مورد علاقه خود باز کنید و main() را به این شکل تبدیل کنید -

int main(int argc, char **argv) {
  absl::ParseCommandLine(argc, argv);

  // Codelab Solution: Register a global gRPC OpenTelemetry plugin configured
  // with a prometheus exporter.
  opentelemetry::exporter::metrics::PrometheusExporterOptions opts;
  opts.url = absl::GetFlag(FLAGS_prometheus_endpoint);
  auto prometheus_exporter =
      opentelemetry::exporter::metrics::PrometheusExporterFactory::Create(opts);
  auto meter_provider =
      std::make_shared<opentelemetry::sdk::metrics::MeterProvider>();

  // The default histogram boundaries are not granular enough for RPCs. Override
  // the "grpc.client.attempt.duration" view as recommended by
  // https://github.com/grpc/proposal/blob/master/A66-otel-stats.md.
  AddLatencyView(meter_provider.get(), "grpc.client.attempt.duration", "s");
  meter_provider->AddMetricReader(std::move(prometheus_exporter));
  auto status = grpc::OpenTelemetryPluginBuilder()
                    .SetMeterProvider(std::move(meter_provider))
                    .BuildAndRegisterGlobal();
  if (!status.ok()) {
    std::cerr << "Failed to register gRPC OpenTelemetry Plugin: "
              << status.ToString() << std::endl;
    return static_cast<int>(status.code());
  }

  // Continuously send RPCs.
  RunClient(absl::GetFlag(FLAGS_target));

  return 0;
}

مرحله بعدی اضافه کردن افزونه OpenTelemetry به سرور است. codelabs/grpc-cpp-opentelemetry/start_here/greeter_callback_server.cc را باز کنید و main را به شکل زیر تغییر دهید:

int main(int argc, char** argv) {
  absl::ParseCommandLine(argc, argv);

  // Register a global gRPC OpenTelemetry plugin configured with a prometheus
  // exporter.
  opentelemetry::exporter::metrics::PrometheusExporterOptions opts;
  opts.url = absl::GetFlag(FLAGS_prometheus_endpoint);
  auto prometheus_exporter =
      opentelemetry::exporter::metrics::PrometheusExporterFactory::Create(opts);
  auto meter_provider =
      std::make_shared<opentelemetry::sdk::metrics::MeterProvider>();

  // The default histogram boundaries are not granular enough for RPCs. Override
  // the "grpc.server.call.duration" view as recommended by
  // https://github.com/grpc/proposal/blob/master/A66-otel-stats.md.
  AddLatencyView(meter_provider.get(), "grpc.server.call.duration", "s");
  meter_provider->AddMetricReader(std::move(prometheus_exporter));
  auto status = grpc::OpenTelemetryPluginBuilder()
                    .SetMeterProvider(std::move(meter_provider))
                    .BuildAndRegisterGlobal();
  if (!status.ok()) {
    std::cerr << "Failed to register gRPC OpenTelemetry Plugin: "
              << status.ToString() << std::endl;
    return static_cast<int>(status.code());
  }
  RunServer(absl::GetFlag(FLAGS_port));
  return 0;
}

فایل‌های هدر مورد نیاز و وابستگی‌های ساخت، برای راحتی کار، از قبل اضافه شده‌اند.

#include "opentelemetry/exporters/prometheus/exporter_factory.h"
#include "opentelemetry/exporters/prometheus/exporter_options.h"
#include "opentelemetry/sdk/metrics/meter_provider.h"

#include <grpcpp/ext/otel_plugin.h>

وابستگی‌های ساخت (Build dependencys) نیز از قبل در فایل BUILD اضافه شده‌اند -

cc_binary(
    name = "greeter_callback_client",
    srcs = ["greeter_callback_client.cc"],
    defines = ["BAZEL_BUILD"],
    deps = [
        "//util:util",
        "@com_github_grpc_grpc//:grpc++",
        "@com_github_grpc_grpc//:grpcpp_otel_plugin",
        "@com_google_absl//absl/flags:flag",
        "@com_google_absl//absl/flags:parse",
        "@io_opentelemetry_cpp//exporters/prometheus:prometheus_exporter",
        "@io_opentelemetry_cpp//sdk/src/metrics",
    ],
)

۴. اجرای مثال و مشاهده معیارها

برای اجرای سرور، دستور زیر را اجرا کنید -

bazel run start_here:greeter_callback_server

با راه‌اندازی موفقیت‌آمیز، خروجی زیر را برای سرور مشاهده خواهید کرد -

Server listening on 0.0.0.0:50051

در حالی که سرور در حال اجرا است، در یک ترمینال دیگر، کلاینت را اجرا کنید -

bazel run start_here:greeter_callback_client

یک اجرای موفق به این شکل خواهد بود -

Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world

از آنجایی که افزونه gRPC OpenTelemetry را برای خروجی گرفتن از معیارها با استفاده از Prometheus تنظیم کرده‌ایم، این معیارها برای سرور در localhost:9464 و برای کلاینت در localhost:9465 در دسترس خواهند بود.

برای دیدن معیارهای مشتری -

curl localhost:9465/metrics

نتیجه به شکل زیر خواهد بود -

# HELP exposer_transferred_bytes_total Transferred bytes to metrics services
# TYPE exposer_transferred_bytes_total counter
exposer_transferred_bytes_total 0
# HELP exposer_scrapes_total Number of times metrics were scraped
# TYPE exposer_scrapes_total counter
exposer_scrapes_total 0
# HELP exposer_request_latencies Latencies of serving scrape requests, in microseconds
# TYPE exposer_request_latencies summary
exposer_request_latencies_count 0
exposer_request_latencies_sum 0
exposer_request_latencies{quantile="0.5"} Nan
exposer_request_latencies{quantile="0.9"} Nan
exposer_request_latencies{quantile="0.99"} Nan
# HELP target Target metadata
# TYPE target gauge
target_info{otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",service_name="unknown_service",telemetry_sdk_version="1.13.0",telemetry_sdk_name="opentelemetry",telemetry_sdk_language="cpp"} 1 1721958543107
# 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_count{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev"} 96 1721958543107
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-c++",otel_scope_version="1.67.0-dev"} 1248 1721958543107
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-c++",otel_scope_version="1.67.0-dev",le="0"} 0 1721958543107
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-c++",otel_scope_version="1.67.0-dev",le="5"} 0 1721958543107
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-c++",otel_scope_version="1.67.0-dev",le="10"} 0 1721958543107
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-c++",otel_scope_version="1.67.0-dev",le="25"} 96 1721958543107
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-c++",otel_scope_version="1.67.0-dev",le="50"} 96 1721958543107
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-c++",otel_scope_version="1.67.0-dev",le="75"} 96 1721958543107

به طور مشابه، برای معیارهای سمت سرور -

curl localhost:9464/metrics

۵. مشاهده معیارها در پرومتئوس

در اینجا، ما یک نمونه پرومتئوس راه‌اندازی خواهیم کرد که کلاینت و سرور نمونه gRPC ما را که معیارها را با استفاده از پرومتئوس صادر می‌کنند، scrap می‌کند.

آخرین نسخه Prometheus را برای پلتفرم خود دانلود کنید ، سپس آن را استخراج و اجرا کنید:

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

یک فایل پیکربندی پرومتئوس با موارد زیر ایجاد کنید -

cat > grpc_otel_cpp_prometheus.yml <<EOF
scrape_configs:
  - job_name: "prometheus"
    scrape_interval: 5s
    static_configs:
      - targets: ["localhost:9090"]
  - job_name: "grpc-otel-cpp"
    scrape_interval: 5s
    static_configs:
      - targets: ["localhost:9464", "localhost:9465"]
EOF

پرومتئوس را با پیکربندی جدید شروع کنید -

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

این کار باعث می‌شود معیارهای مربوط به فرآیندهای codelab کلاینت و سرور هر ۵ ثانیه یکبار scrap شوند.

برای مشاهده معیارها به آدرس http://localhost:9090/graph بروید. برای مثال، عبارت زیر

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

نموداری با میانه تأخیر تلاش با استفاده از ۱ دقیقه به عنوان بازه زمانی برای محاسبه چندک نشان می‌دهد.

نرخ درخواست‌ها -

increase(grpc_client_attempt_duration_seconds_bucket[1m])

۶. (اختیاری) تمرین برای کاربر

در داشبوردهای پرومتئوس، متوجه خواهید شد که QPS پایین است. ببینید آیا می‌توانید کد مشکوکی را در مثال شناسایی کنید که QPS را محدود می‌کند یا خیر.

برای علاقه‌مندان، کد کلاینت خود را به داشتن تنها یک RPC در حال انتظار در یک لحظه معین محدود می‌کند. این می‌تواند به گونه‌ای اصلاح شود که کلاینت RPC های بیشتری را بدون انتظار برای تکمیل RPC های قبلی ارسال کند. (راه حلی برای این مشکل ارائه نشده است.)