مقاييس Prometheus في Rust: مراقبة الأداء
Prometheus هو المعيار الفعلي لجمع وتحليل المقاييس في البنى التحتية للخدمات المصغرة الحديثة. في نظام Rust البيئي، توجد عدة مكتبات ناضجة تسمح بدمج تصدير المقاييس بتنسيق Prometheus بسهولة. في هذه المقالة، سنستعرض مكتبتين رئيسيتين: prometheus (العميل الرسمي) و metrics-exporter-prometheus (جزء من إطار العمل metrics).
لماذا نحتاج إلى المقاييس في Rust؟
مراقبة تطبيقات Rust أمر بالغ الأهمية من أجل:
- تتبع الأداء (زمن الاستجابة، الإنتاجية)
- اكتشاف تسرب الذاكرة ومشاكل الموارد
- إعداد التنبيهات بناءً على القيم الحدية
- تحليل الاتجاهات والتخطيط للسعة
مكتبة prometheus (prometheus-rs)
عميل Rust الرسمي لـ Prometheus. يوفر مجموعة كاملة من الأدوات لإنشاء وتصدير المقاييس.
التثبيت
أضف إلى Cargo.toml:
[dependencies]prometheus = { version = "0.13", features = ["process"] }hyper = { version = "0.14", features = ["http1", "server"] }مثال أساسي: خادم HTTP مع مقاييس
use prometheus::{Counter, HistogramOpts, HistogramVec, IntCounter, Registry, TextEncoder};use std::net::SocketAddr;use hyper::{Body, Request, Response, Server};use hyper::service::{make_service_fn, service_fn};use std::convert::Infallible;use std::sync::Arc;use tokio::sync::Mutex;
#[tokio::main]async fn main() { let registry = Registry::new();
// إنشاء عداد الطلبات let request_counter = IntCounter::new("http_requests_total", "Total HTTP requests") .unwrap(); registry.register(Box::new(request_counter.clone())).unwrap();
// مدرج تكراري لزمن الاستجابة let request_duration = HistogramVec::new( HistogramOpts::new("http_request_duration_seconds", "HTTP request duration"), &["method", "path"] ).unwrap(); registry.register(Box::new(request_duration.clone())).unwrap();
let data = Arc::new(Mutex::new((request_counter, request_duration)));
let make_svc = make_service_fn(move |_conn| { let data = data.clone(); async move { Ok::<_, Infallible>(service_fn(move |req: Request| { let data = data.clone(); async move { let start = std::time::Instant::now(); let (counter, duration) = &*data.lock().await; counter.inc();
// معالجة الطلب let response = match req.uri().path() { "/metrics" => { let encoder = TextEncoder::new(); let metric_families = registry.gather(); let mut buffer = vec![]; encoder.encode(&metric_families, &mut buffer).unwrap(); Response::new(Body::from(buffer)) } "/" => Response::new(Body::from("Hello, World!")), _ => Response::builder() .status(404) .body(Body::from("Not Found")) .unwrap(), };
let elapsed = start.elapsed().as_secs_f64(); duration .with_label_values(&[req.method().as_str(), req.uri().path()]) .observe(elapsed);
Ok::<_, Infallible>(response) } })) } });
let addr = SocketAddr::from(([127, 0, 0, 1], 8080)); let server = Server::bind(&addr).serve(make_svc); println!("Listening on http://{}", addr); if let Err(e) = server.a