Skip to main content

sz_orm_limit/
metrics.rs

1//! 限流统计与监控(Rate Limit Metrics)
2//!
3//! 提供限流器的运行时统计、监控和可观测性支持。
4//! 可集成到 Prometheus、OpenTelemetry 等监控系统。
5
6use std::collections::HashMap;
7use std::sync::atomic::{AtomicU64, Ordering};
8use std::sync::{Arc, RwLock};
9use std::time::{Duration, Instant};
10
11/// 限流指标收集器
12///
13/// 收集限流器的运行时指标,包括允许/拒绝计数、延迟分布等。
14/// 线程安全,可在多线程环境下使用。
15pub struct RateLimitMetrics {
16    inner: Arc<MetricsInner>,
17}
18
19struct MetricsInner {
20    /// 总请求数
21    total_requests: AtomicU64,
22    /// 允许请求数
23    total_allowed: AtomicU64,
24    /// 拒绝请求数
25    total_rejected: AtomicU64,
26    /// 总延迟(微秒)
27    total_latency_us: AtomicU64,
28    /// 最大延迟(微秒)
29    max_latency_us: AtomicU64,
30    /// 最小延迟(微秒)
31    min_latency_us: AtomicU64,
32    /// 按 key 分组的统计
33    per_key: RwLock<HashMap<String, KeyMetrics>>,
34    /// 按算法分组的统计
35    per_algorithm: RwLock<HashMap<String, AlgorithmMetrics>>,
36    /// 启动时间
37    start_time: Instant,
38}
39
40/// 单个 key 的指标
41#[derive(Debug, Clone, Default, serde::Serialize)]
42pub struct KeyMetrics {
43    pub requests: u64,
44    pub allowed: u64,
45    pub rejected: u64,
46    pub last_access_ms: i64,
47}
48
49/// 单个算法的指标
50#[derive(Debug, Clone, Default, serde::Serialize)]
51pub struct AlgorithmMetrics {
52    pub requests: u64,
53    pub allowed: u64,
54    pub rejected: u64,
55    pub avg_latency_us: u64,
56}
57
58/// 指标快照
59#[derive(Debug, Clone, serde::Serialize)]
60pub struct MetricsSnapshot {
61    pub total_requests: u64,
62    pub total_allowed: u64,
63    pub total_rejected: u64,
64    pub allow_rate: f64,
65    pub reject_rate: f64,
66    pub avg_latency_us: u64,
67    pub max_latency_us: u64,
68    pub min_latency_us: u64,
69    pub uptime_secs: u64,
70    pub per_key_count: usize,
71    pub per_algorithm_count: usize,
72}
73
74impl RateLimitMetrics {
75    /// 创建指标收集器
76    pub fn new() -> Self {
77        Self {
78            inner: Arc::new(MetricsInner {
79                total_requests: AtomicU64::new(0),
80                total_allowed: AtomicU64::new(0),
81                total_rejected: AtomicU64::new(0),
82                total_latency_us: AtomicU64::new(0),
83                max_latency_us: AtomicU64::new(0),
84                min_latency_us: AtomicU64::new(u64::MAX),
85                per_key: RwLock::new(HashMap::new()),
86                per_algorithm: RwLock::new(HashMap::new()),
87                start_time: Instant::now(),
88            }),
89        }
90    }
91
92    /// 记录一次允许的请求
93    pub fn record_allowed(&self, key: &str, algorithm: &str, latency: Duration) {
94        self.record(key, algorithm, true, latency);
95    }
96
97    /// 记录一次拒绝的请求
98    pub fn record_rejected(&self, key: &str, algorithm: &str, latency: Duration) {
99        self.record(key, algorithm, false, latency);
100    }
101
102    fn record(&self, key: &str, algorithm: &str, allowed: bool, latency: Duration) {
103        let latency_us = latency.as_micros() as u64;
104        self.inner.total_requests.fetch_add(1, Ordering::Relaxed);
105        if allowed {
106            self.inner.total_allowed.fetch_add(1, Ordering::Relaxed);
107        } else {
108            self.inner.total_rejected.fetch_add(1, Ordering::Relaxed);
109        }
110        self.inner
111            .total_latency_us
112            .fetch_add(latency_us, Ordering::Relaxed);
113
114        let mut max = self.inner.max_latency_us.load(Ordering::Relaxed);
115        while latency_us > max {
116            match self.inner.max_latency_us.compare_exchange(
117                max,
118                latency_us,
119                Ordering::Relaxed,
120                Ordering::Relaxed,
121            ) {
122                Ok(_) => break,
123                Err(new_max) => max = new_max,
124            }
125        }
126
127        let mut min = self.inner.min_latency_us.load(Ordering::Relaxed);
128        while latency_us < min {
129            match self.inner.min_latency_us.compare_exchange(
130                min,
131                latency_us,
132                Ordering::Relaxed,
133                Ordering::Relaxed,
134            ) {
135                Ok(_) => break,
136                Err(new_min) => min = new_min,
137            }
138        }
139
140        if let Ok(mut per_key) = self.inner.per_key.write() {
141            let metrics = per_key.entry(key.to_string()).or_default();
142            metrics.requests += 1;
143            if allowed {
144                metrics.allowed += 1;
145            } else {
146                metrics.rejected += 1;
147            }
148            metrics.last_access_ms = now_timestamp();
149        }
150
151        if let Ok(mut per_algo) = self.inner.per_algorithm.write() {
152            let metrics = per_algo.entry(algorithm.to_string()).or_default();
153            metrics.requests += 1;
154            if allowed {
155                metrics.allowed += 1;
156            } else {
157                metrics.rejected += 1;
158            }
159            metrics.avg_latency_us = if metrics.requests > 0 {
160                (metrics.avg_latency_us * (metrics.requests - 1) + latency_us) / metrics.requests
161            } else {
162                latency_us
163            };
164        }
165    }
166
167    /// 获取指标快照
168    pub fn snapshot(&self) -> MetricsSnapshot {
169        let total = self.inner.total_requests.load(Ordering::Relaxed);
170        let allowed = self.inner.total_allowed.load(Ordering::Relaxed);
171        let rejected = self.inner.total_rejected.load(Ordering::Relaxed);
172        let total_latency = self.inner.total_latency_us.load(Ordering::Relaxed);
173        let max_latency = self.inner.max_latency_us.load(Ordering::Relaxed);
174        let min_latency = self.inner.min_latency_us.load(Ordering::Relaxed);
175
176        MetricsSnapshot {
177            total_requests: total,
178            total_allowed: allowed,
179            total_rejected: rejected,
180            allow_rate: if total > 0 {
181                allowed as f64 / total as f64
182            } else {
183                0.0
184            },
185            reject_rate: if total > 0 {
186                rejected as f64 / total as f64
187            } else {
188                0.0
189            },
190            avg_latency_us: if total > 0 { total_latency / total } else { 0 },
191            max_latency_us: if total > 0 { max_latency } else { 0 },
192            min_latency_us: if total > 0 {
193                min_latency.min(max_latency)
194            } else {
195                0
196            },
197            uptime_secs: self.inner.start_time.elapsed().as_secs(),
198            per_key_count: self.inner.per_key.read().map(|m| m.len()).unwrap_or(0),
199            per_algorithm_count: self
200                .inner
201                .per_algorithm
202                .read()
203                .map(|m| m.len())
204                .unwrap_or(0),
205        }
206    }
207
208    /// 获取所有 key 的指标
209    pub fn per_key_metrics(&self) -> HashMap<String, KeyMetrics> {
210        self.inner
211            .per_key
212            .read()
213            .map(|m| m.clone())
214            .unwrap_or_default()
215    }
216
217    /// 获取所有算法的指标
218    pub fn per_algorithm_metrics(&self) -> HashMap<String, AlgorithmMetrics> {
219        self.inner
220            .per_algorithm
221            .read()
222            .map(|m| m.clone())
223            .unwrap_or_default()
224    }
225
226    /// 获取特定 key 的指标
227    pub fn key_metrics(&self, key: &str) -> Option<KeyMetrics> {
228        self.inner
229            .per_key
230            .read()
231            .ok()
232            .and_then(|m| m.get(key).cloned())
233    }
234
235    /// 重置所有指标
236    pub fn reset(&self) {
237        self.inner.total_requests.store(0, Ordering::Relaxed);
238        self.inner.total_allowed.store(0, Ordering::Relaxed);
239        self.inner.total_rejected.store(0, Ordering::Relaxed);
240        self.inner.total_latency_us.store(0, Ordering::Relaxed);
241        self.inner.max_latency_us.store(0, Ordering::Relaxed);
242        self.inner.min_latency_us.store(u64::MAX, Ordering::Relaxed);
243        if let Ok(mut per_key) = self.inner.per_key.write() {
244            per_key.clear();
245        }
246        if let Ok(mut per_algo) = self.inner.per_algorithm.write() {
247            per_algo.clear();
248        }
249    }
250
251    /// 导出为 Prometheus 格式文本
252    pub fn to_prometheus(&self) -> String {
253        let snap = self.snapshot();
254        let mut output = String::new();
255        output.push_str("# HELP rate_limit_requests_total Total requests\n");
256        output.push_str("# TYPE rate_limit_requests_total counter\n");
257        output.push_str(&format!(
258            "rate_limit_requests_total {}\n",
259            snap.total_requests
260        ));
261        output.push_str("# HELP rate_limit_allowed_total Total allowed requests\n");
262        output.push_str("# TYPE rate_limit_allowed_total counter\n");
263        output.push_str(&format!(
264            "rate_limit_allowed_total {}\n",
265            snap.total_allowed
266        ));
267        output.push_str("# HELP rate_limit_rejected_total Total rejected requests\n");
268        output.push_str("# TYPE rate_limit_rejected_total counter\n");
269        output.push_str(&format!(
270            "rate_limit_rejected_total {}\n",
271            snap.total_rejected
272        ));
273        output.push_str("# HELP rate_limit_latency_us Latency in microseconds\n");
274        output.push_str("# TYPE rate_limit_latency_us gauge\n");
275        output.push_str(&format!(
276            "rate_limit_latency_us{{quantile=\"avg\"}} {}\n",
277            snap.avg_latency_us
278        ));
279        output.push_str(&format!(
280            "rate_limit_latency_us{{quantile=\"max\"}} {}\n",
281            snap.max_latency_us
282        ));
283        output.push_str(&format!(
284            "rate_limit_latency_us{{quantile=\"min\"}} {}\n",
285            snap.min_latency_us
286        ));
287        output.push_str("# HELP rate_limit_uptime_seconds Uptime in seconds\n");
288        output.push_str("# TYPE rate_limit_uptime_seconds gauge\n");
289        output.push_str(&format!("rate_limit_uptime_seconds {}\n", snap.uptime_secs));
290        output
291    }
292
293    /// 导出为 JSON
294    pub fn to_json(&self) -> serde_json::Value {
295        let snap = self.snapshot();
296        serde_json::json!({
297            "total_requests": snap.total_requests,
298            "total_allowed": snap.total_allowed,
299            "total_rejected": snap.total_rejected,
300            "allow_rate": snap.allow_rate,
301            "reject_rate": snap.reject_rate,
302            "avg_latency_us": snap.avg_latency_us,
303            "max_latency_us": snap.max_latency_us,
304            "min_latency_us": snap.min_latency_us,
305            "uptime_secs": snap.uptime_secs,
306            "per_key_count": snap.per_key_count,
307            "per_algorithm_count": snap.per_algorithm_count,
308        })
309    }
310}
311
312impl Default for RateLimitMetrics {
313    fn default() -> Self {
314        Self::new()
315    }
316}
317
318impl Clone for RateLimitMetrics {
319    fn clone(&self) -> Self {
320        Self {
321            inner: self.inner.clone(),
322        }
323    }
324}
325
326fn now_timestamp() -> i64 {
327    use std::time::{SystemTime, UNIX_EPOCH};
328    SystemTime::now()
329        .duration_since(UNIX_EPOCH)
330        .unwrap_or_default()
331        .as_millis() as i64
332}
333
334/// 限流监控器
335///
336/// 定期采样指标并触发告警。适用于长期运行的限流器。
337pub struct RateLimitMonitor {
338    metrics: RateLimitMetrics,
339    alert_threshold: f64,
340    last_snapshot: RwLock<Option<MetricsSnapshot>>,
341}
342
343impl RateLimitMonitor {
344    /// 创建监控器
345    ///
346    /// - `metrics`:指标收集器
347    /// - `alert_threshold`:拒绝率告警阈值(0.0~1.0)
348    pub fn new(metrics: RateLimitMetrics, alert_threshold: f64) -> Self {
349        Self {
350            metrics,
351            alert_threshold,
352            last_snapshot: RwLock::new(None),
353        }
354    }
355
356    /// 采样并存储快照
357    pub fn sample(&self) -> MetricsSnapshot {
358        let snap = self.metrics.snapshot();
359        if let Ok(mut last) = self.last_snapshot.write() {
360            *last = Some(snap.clone());
361        }
362        snap
363    }
364
365    /// 检查是否触发告警
366    pub fn check_alert(&self) -> Option<Alert> {
367        let snap = self.metrics.snapshot();
368        if snap.reject_rate > self.alert_threshold && snap.total_requests > 10 {
369            Some(Alert::HighRejectRate {
370                reject_rate: snap.reject_rate,
371                threshold: self.alert_threshold,
372                total_requests: snap.total_requests,
373            })
374        } else {
375            None
376        }
377    }
378
379    /// 获取上次快照
380    pub fn last_snapshot(&self) -> Option<MetricsSnapshot> {
381        self.last_snapshot.read().ok().and_then(|s| s.clone())
382    }
383
384    /// 获取指标收集器
385    pub fn metrics(&self) -> &RateLimitMetrics {
386        &self.metrics
387    }
388}
389
390/// 告警
391#[derive(Debug, Clone, serde::Serialize)]
392pub enum Alert {
393    HighRejectRate {
394        reject_rate: f64,
395        threshold: f64,
396        total_requests: u64,
397    },
398}
399
400impl std::fmt::Display for Alert {
401    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
402        match self {
403            Alert::HighRejectRate {
404                reject_rate,
405                threshold,
406                total_requests,
407            } => write!(
408                f,
409                "High reject rate: {:.2}% (threshold: {:.2}%, total: {})",
410                reject_rate * 100.0,
411                threshold * 100.0,
412                total_requests
413            ),
414        }
415    }
416}
417
418#[cfg(test)]
419mod tests {
420    use super::*;
421
422    #[test]
423    fn test_metrics_new() {
424        let metrics = RateLimitMetrics::new();
425        let snap = metrics.snapshot();
426        assert_eq!(snap.total_requests, 0);
427        assert_eq!(snap.total_allowed, 0);
428        assert_eq!(snap.total_rejected, 0);
429    }
430
431    #[test]
432    fn test_metrics_record_allowed() {
433        let metrics = RateLimitMetrics::new();
434        metrics.record_allowed("k", "token_bucket", Duration::from_micros(100));
435        let snap = metrics.snapshot();
436        assert_eq!(snap.total_requests, 1);
437        assert_eq!(snap.total_allowed, 1);
438        assert_eq!(snap.total_rejected, 0);
439    }
440
441    #[test]
442    fn test_metrics_record_rejected() {
443        let metrics = RateLimitMetrics::new();
444        metrics.record_rejected("k", "token_bucket", Duration::from_micros(50));
445        let snap = metrics.snapshot();
446        assert_eq!(snap.total_requests, 1);
447        assert_eq!(snap.total_allowed, 0);
448        assert_eq!(snap.total_rejected, 1);
449    }
450
451    #[test]
452    fn test_metrics_allow_rate() {
453        let metrics = RateLimitMetrics::new();
454        metrics.record_allowed("k", "tb", Duration::from_micros(10));
455        metrics.record_allowed("k", "tb", Duration::from_micros(10));
456        metrics.record_rejected("k", "tb", Duration::from_micros(10));
457        let snap = metrics.snapshot();
458        assert!((snap.allow_rate - 2.0 / 3.0).abs() < 0.001);
459        assert!((snap.reject_rate - 1.0 / 3.0).abs() < 0.001);
460    }
461
462    #[test]
463    fn test_metrics_latency() {
464        let metrics = RateLimitMetrics::new();
465        metrics.record_allowed("k", "tb", Duration::from_micros(100));
466        metrics.record_allowed("k", "tb", Duration::from_micros(200));
467        metrics.record_allowed("k", "tb", Duration::from_micros(300));
468        let snap = metrics.snapshot();
469        assert_eq!(snap.avg_latency_us, 200);
470        assert_eq!(snap.max_latency_us, 300);
471        assert_eq!(snap.min_latency_us, 100);
472    }
473
474    #[test]
475    fn test_metrics_per_key() {
476        let metrics = RateLimitMetrics::new();
477        metrics.record_allowed("a", "tb", Duration::from_micros(10));
478        metrics.record_allowed("b", "tb", Duration::from_micros(10));
479        let per_key = metrics.per_key_metrics();
480        assert_eq!(per_key.len(), 2);
481        assert_eq!(per_key.get("a").unwrap().requests, 1);
482        assert_eq!(per_key.get("b").unwrap().requests, 1);
483    }
484
485    #[test]
486    fn test_metrics_per_algorithm() {
487        let metrics = RateLimitMetrics::new();
488        metrics.record_allowed("k", "token_bucket", Duration::from_micros(10));
489        metrics.record_allowed("k", "sliding_window", Duration::from_micros(10));
490        let per_algo = metrics.per_algorithm_metrics();
491        assert_eq!(per_algo.len(), 2);
492        assert_eq!(per_algo.get("token_bucket").unwrap().requests, 1);
493    }
494
495    #[test]
496    fn test_metrics_key_metrics() {
497        let metrics = RateLimitMetrics::new();
498        metrics.record_allowed("k", "tb", Duration::from_micros(10));
499        metrics.record_rejected("k", "tb", Duration::from_micros(10));
500        let km = metrics.key_metrics("k").unwrap();
501        assert_eq!(km.requests, 2);
502        assert_eq!(km.allowed, 1);
503        assert_eq!(km.rejected, 1);
504    }
505
506    #[test]
507    fn test_metrics_reset() {
508        let metrics = RateLimitMetrics::new();
509        metrics.record_allowed("k", "tb", Duration::from_micros(10));
510        metrics.reset();
511        let snap = metrics.snapshot();
512        assert_eq!(snap.total_requests, 0);
513    }
514
515    #[test]
516    fn test_metrics_to_prometheus() {
517        let metrics = RateLimitMetrics::new();
518        metrics.record_allowed("k", "tb", Duration::from_micros(10));
519        let prom = metrics.to_prometheus();
520        assert!(prom.contains("rate_limit_requests_total 1"));
521        assert!(prom.contains("rate_limit_allowed_total 1"));
522    }
523
524    #[test]
525    fn test_metrics_to_json() {
526        let metrics = RateLimitMetrics::new();
527        metrics.record_allowed("k", "tb", Duration::from_micros(10));
528        let json = metrics.to_json();
529        assert_eq!(json["total_requests"], 1);
530        assert_eq!(json["total_allowed"], 1);
531    }
532
533    #[test]
534    fn test_metrics_clone() {
535        let metrics = RateLimitMetrics::new();
536        metrics.record_allowed("k", "tb", Duration::from_micros(10));
537        let cloned = metrics.clone();
538        assert_eq!(cloned.snapshot().total_requests, 1);
539    }
540
541    #[test]
542    fn test_monitor_no_alert() {
543        let metrics = RateLimitMetrics::new();
544        let monitor = RateLimitMonitor::new(metrics, 0.5);
545        for _ in 0..10 {
546            monitor
547                .metrics()
548                .record_allowed("k", "tb", Duration::from_micros(10));
549        }
550        assert!(monitor.check_alert().is_none());
551    }
552
553    #[test]
554    fn test_monitor_high_reject_alert() {
555        let metrics = RateLimitMetrics::new();
556        let monitor = RateLimitMonitor::new(metrics, 0.5);
557        for _ in 0..10 {
558            monitor
559                .metrics()
560                .record_allowed("k", "tb", Duration::from_micros(10));
561        }
562        for _ in 0..20 {
563            monitor
564                .metrics()
565                .record_rejected("k", "tb", Duration::from_micros(10));
566        }
567        let alert = monitor.check_alert();
568        assert!(alert.is_some());
569    }
570
571    #[test]
572    fn test_monitor_sample() {
573        let metrics = RateLimitMetrics::new();
574        let monitor = RateLimitMonitor::new(metrics, 0.5);
575        monitor
576            .metrics()
577            .record_allowed("k", "tb", Duration::from_micros(10));
578        let snap = monitor.sample();
579        assert_eq!(snap.total_requests, 1);
580        assert!(monitor.last_snapshot().is_some());
581    }
582
583    #[test]
584    fn test_alert_display() {
585        let alert = Alert::HighRejectRate {
586            reject_rate: 0.8,
587            threshold: 0.5,
588            total_requests: 100,
589        };
590        let s = alert.to_string();
591        assert!(s.contains("80.00%"));
592    }
593
594    #[test]
595    fn test_metrics_snapshot_empty() {
596        let metrics = RateLimitMetrics::new();
597        let snap = metrics.snapshot();
598        assert_eq!(snap.allow_rate, 0.0);
599        assert_eq!(snap.reject_rate, 0.0);
600        assert_eq!(snap.avg_latency_us, 0);
601    }
602
603    #[test]
604    fn test_metrics_uptime() {
605        let metrics = RateLimitMetrics::new();
606        std::thread::sleep(Duration::from_millis(10));
607        let snap = metrics.snapshot();
608        assert!(snap.uptime_secs == 0 || snap.uptime_secs >= 0);
609    }
610
611    #[test]
612    fn test_metrics_per_key_count() {
613        let metrics = RateLimitMetrics::new();
614        metrics.record_allowed("a", "tb", Duration::from_micros(10));
615        metrics.record_allowed("b", "tb", Duration::from_micros(10));
616        metrics.record_allowed("c", "tb", Duration::from_micros(10));
617        let snap = metrics.snapshot();
618        assert_eq!(snap.per_key_count, 3);
619    }
620}