gRPC C++ তে বেসিক OpenTelemetry প্লাগইন সেটআপ করুন

১. ভূমিকা

এই কোডল্যাবে, আপনি gRPC ব্যবহার করে একটি ক্লায়েন্ট ও সার্ভার তৈরি করবেন, যা C++ এ লেখা একটি রাউট-ম্যাপিং অ্যাপ্লিকেশনের ভিত্তি তৈরি করবে।

এই টিউটোরিয়ালটি শেষে, আপনি gRPC OpenTelemetry প্লাগইন দ্বারা সজ্জিত একটি সাধারণ gRPC HelloWorld অ্যাপ্লিকেশন তৈরি করতে পারবেন এবং Prometheus-এ এক্সপোর্ট করা অবজার্ভেবিলিটি মেট্রিকগুলো দেখতে সক্ষম হবেন।

আপনি যা শিখবেন

  • বিদ্যমান gRPC C++ অ্যাপ্লিকেশনের জন্য OpenTelemetry প্লাগইন কীভাবে সেটআপ করবেন
  • স্থানীয় প্রমিথিউস ইনস্ট্যান্স চালানো হচ্ছে
  • প্রমিথিউসে মেট্রিক্স রপ্তানি করা
  • প্রমিথিউস ড্যাশবোর্ড থেকে মেট্রিক্স দেখুন

২. শুরু করার আগে

আপনার যা যা লাগবে

  • 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

bazelisk এর মাধ্যমে bazel ইনস্টল করা যায়। এর সর্বশেষ সংস্করণটি এখানে পাওয়া যাবে।

এটি সেট আপ করার একটি সহজ উপায় হলো, এটিকে আপনার PATH-এ bazel বাইনারি হিসেবে নিম্নোক্তভাবে ইনস্টল করা:

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

বিকল্পভাবে, আপনি CMake-ও ব্যবহার করতে পারেন। CMake ব্যবহারের নির্দেশাবলী এখানে পাওয়া যাবে।

কোডটি নিন

আপনার শেখার প্রক্রিয়াকে সহজ করতে, এই কোডল্যাবটি আপনাকে কাজ শুরু করার জন্য একটি পূর্ব-নির্মিত সোর্স কোড কাঠামো প্রদান করে। নিম্নলিখিত ধাপগুলো আপনাকে একটি অ্যাপ্লিকেশনে gRPC OpenTelemetry প্লাগইনটি ইন্সট্রুমেন্ট করার বিষয়ে নির্দেশনা দেবে।

grpc-codelabs

এই কোডল্যাবের স্কাফোল্ড সোর্স কোড এই গিটহাব ডিরেক্টরিতে পাওয়া যাচ্ছে। আপনি যদি নিজে কোডটি ইমপ্লিমেন্ট করতে না চান, তবে সম্পূর্ণ সোর্স কোডটি completed ডিরেক্টরিতে পাওয়া যাবে।

প্রথমে, grpc কোডল্যাব রিপোটি ক্লোন করুন এবং grpc-cpp-opentelemetry ফোল্ডারে প্রবেশ করুন:

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

বিকল্পভাবে, আপনি শুধু কোডল্যাব ডিরেক্টরি সম্বলিত .zip ফাইলটি ডাউনলোড করে ম্যানুয়ালি আনজিপ করতে পারেন।

bazel ব্যবহার করে gRPC লাইব্রেরিটি বিল্ড করুন:

bazel build start_here/...

৩. ওপেনটেলিমেট্রি প্লাগইনটি নিবন্ধন করুন

gRPC OpenTelemetry প্লাগইনটি যুক্ত করার জন্য আমাদের একটি gRPC অ্যাপ্লিকেশন প্রয়োজন। এই কোডল্যাবে, আমরা একটি সাধারণ gRPC HelloWorld ক্লায়েন্ট এবং সার্ভার ব্যবহার করব, যেটিকে আমরা gRPC OpenTelemetry প্লাগইন দিয়ে ইন্সট্রুমেন্ট করব।

আপনার প্রথম পদক্ষেপ হলো ক্লায়েন্টে প্রমিথিউস এক্সপোর্টার দিয়ে কনফিগার করা ওপেনটেলিমেট্রি প্লাগইনটি রেজিস্টার করা। আপনার পছন্দের এডিটর দিয়ে 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 ফাইলে ইতিমধ্যে যোগ করা হয়েছে -

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 প্লাগইনটি সেট-আপ করেছি, সেই মেট্রিক্সগুলো সার্ভারের জন্য 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 উদাহরণ ক্লায়েন্ট এবং সার্ভার থেকে মেট্রিক্স স্ক্র্যাপ করবে, যেগুলো প্রোমিথিউস ব্যবহার করে মেট্রিক্স এক্সপোর্ট করছে।

আপনার প্ল্যাটফর্মের জন্য প্রোমিথিউসের সর্বশেষ রিলিজটি ডাউনলোড করুন , তারপর এটি এক্সট্র্যাক্ট করে চালান:

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

এটি ক্লায়েন্ট এবং সার্ভার কোডল্যাব প্রসেসগুলো থেকে প্রতি ৫ সেকেন্ড পর পর মেট্রিক্স স্ক্র্যাপ করার জন্য কনফিগার করবে।

মেট্রিকগুলো দেখতে 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 পাঠাতে পারে। (এর সমাধান এখনও প্রদান করা হয়নি।)