Überwachung von Rust-Anwendungen: Metriken und Alarme auf fortgeschrittenem Niveau

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Warum die Überwachung von Rust-Anwendungen eine Herausforderung und Chance ist



Rust ist bekannt für seine Leistung und Speichersicherheit. Wenn Ihre Anwendung jedoch an den Grenzen der Hardware arbeitet (z. B. ein hochbelasteter WebSocket-Server oder ein Datenparser), reichen die Standardmetriken wie CPU und RAM oft nicht aus. Auf fortgeschrittener Ebene erfordert die Überwachung von Rust-Anwendungen nicht nur die Datenerfassung, sondern die Erstellung eines präzisen Alarmsystems, das einen Ausfall vorhersagt, bevor er eintritt.



In diesem Artikel betrachten wir, wie Sie ein produktionsreifes Überwachungssystem für Rust aufbauen: von benutzerdefinierten Metriken mit der prometheus-Crate bis zur Einrichtung von Alarmen über Alertmanager und Grafana.



1. Fortgeschrittene Metriken: Nicht nur HTTP-Anfragen



Die grundlegende Überwachung (Anzahl der Anfragen, Latenzen) ist das Minimum. Auf fortgeschrittener Ebene sammeln wir Metriken, die den inneren Zustand der Anwendung widerspiegeln: Warteschlangengrößen, Anzahl aktiver Tokio-Aufgaben, Zustand des Verbindungspools.



1.1. Benutzerdefinierte Metriken mit Histogram- und Gauge-Typen



Wir verwenden die Crate prometheus. Betrachten wir ein Beispiel für die Sammlung von Metriken für einen Datenbank-Verbindungspool:



use prometheus::{HistogramOpts, HistogramVec, Gauge, register_histogram_vec, register_gauge};use lazy_static::lazy_static;

lazy_static! { static ref DB_CONNECTION_POOL: Gauge = register_gauge!( "db_connection_pool_size", "Aktuelle Anzahl aktiver Verbindungen im Pool" ).unwrap();

static ref DB_QUERY_DURATION: HistogramVec = register_histogram_vec!( HistogramOpts::new("db_query_duration_seconds", "Ausführungszeit der Datenbankabfrage"), &["query_type", "status"] ).unwrap();}

// Verwendung im Codefn execute_query(query_type: &str) -> Result<(), String> { DB_CONNECTION_POOL.inc(); let start = std::time::Instant::now(); // ... Abfrage ausführen ... let duration = start.elapsed().as_secs_f64(); DB_QUERY_DURATION.with_label_values(&[query_type, "success"]).observe(duration); DB_CONNECTION_POOL.dec(); Ok(())}


Wichtig: Verwenden Sie HistogramVec, um Abfragen nach Typ (SELECT, INSERT) und Status (success, error) zu kategorisieren. Dies ermöglicht die Erstellung von Heatmaps in Grafana.



1.2. Metriken aus dem Tokio-Runtime



Für asynchrone Anwendungen ist es entscheidend, die Auslastung des Schedulers zu kennen. Verwenden Sie tokio-metrics:



use tokio_metrics::RuntimeMetrics;use std::sync::Arc;

async fn monitor_runtime(metrics: Arc<RuntimeMetrics>) { loop { tokio::time::sleep(std::time::Duration::from_secs(5)).await; let num_alive_tasks = metrics.num_alive_tasks(); let blocking_threads = metrics.num_blocking_threads(); // Diese Werte über Gauge an Prometheus senden println!("Aktive Aufgaben: {}, Blockierende Threads: {}", num_alive_tasks, blocking_threads); }}


Diese Metriken helfen, Task-Leaks zu erkennen (wenn num_alive_tasks ständig steigt) oder eine übermäßige Nutzung von block_in_place.



2. Architektur der Metrikenerfassung: Push vs Pull



Im Rust-Ökosystem wird häufiger das Pull-Modell (Prometheus Scraper) verwendet, aber für kurzlebige Prozesse (CLI-Tools, Parser) ist der Push-Ansatz über Pushgateway praktisch.



2.1. Pull-Modell: HTTP-Endpunkt



Die Standardmethode besteht darin, einen separaten HTTP-Server auf Port 9090 (oder einem anderen) innerhalb Ihrer Anwendung zu starten:



use warp::Filter;use prometheus::{Encoder, TextEncoder};

async fn metrics_handler() -> Result<impl warp::Reply, warp::Rejection> { let metric_families = prometheus::gather(); let mut buffer = Vec::new(); let encoder = TextEncoder::new(); encoder.encode(&metric_families, &mut buffer).unwrap(); Ok(warp::reply::with_header(bu

Blogs

Buchempfehlungen