use crate::error::FlowGuardError;
use ahash::AHashMap as HashMap;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::VecDeque;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use tokio::sync::RwLock;
use tracing::{debug, error, info, warn};
#[async_trait]
pub trait CustomLimiter: Send + Sync {
fn name(&self) -> &str;
async fn allow(&self, cost: u64) -> Result<bool, FlowGuardError>;
fn load_config(&mut self, config: Value) -> Result<(), FlowGuardError>;
fn stats(&self) -> LimiterStats;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LimiterStats {
pub total_requests: u64,
pub allowed_requests: u64,
pub rejected_requests: u64,
}
impl LimiterStats {
pub fn new() -> Self {
Self {
total_requests: 0,
allowed_requests: 0,
rejected_requests: 0,
}
}
pub fn rejection_rate(&self) -> f64 {
if self.total_requests == 0 {
0.0
} else {
self.rejected_requests as f64 / self.total_requests as f64
}
}
pub fn allow_rate(&self) -> f64 {
if self.total_requests == 0 {
0.0
} else {
self.allowed_requests as f64 / self.total_requests as f64
}
}
}
impl Default for LimiterStats {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone)]
pub struct CustomLimiterRegistry {
limiters: Arc<RwLock<HashMap<String, Box<dyn CustomLimiter>>>>,
}
impl std::fmt::Debug for CustomLimiterRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CustomLimiterRegistry")
.field("limiters", &"<custom limiters>")
.finish()
}
}
impl CustomLimiterRegistry {
pub fn new() -> Self {
Self {
limiters: Arc::new(RwLock::new(HashMap::new())),
}
}
pub async fn register(
&self,
name: String,
limiter: Box<dyn CustomLimiter>,
) -> Result<(), FlowGuardError> {
let mut limiters = self.limiters.write().await;
if limiters.contains_key(&name) {
let error_msg = format!("限流器 '{}' 已存在", name);
warn!("{}", error_msg);
return Err(FlowGuardError::ConfigError(error_msg));
}
info!("注册自定义限流器: {}", name);
limiters.insert(name.clone(), limiter);
debug!("当前注册的限流器数量: {}", limiters.len());
Ok(())
}
pub async fn get(&self, name: &str) -> Option<Box<dyn CustomLimiter>> {
let limiters = self.limiters.read().await;
if let Some(_limiter) = limiters.get(name) {
debug!("查询限流器: {}", name);
None
} else {
debug!("未找到限流器: {}", name);
None
}
}
pub async fn contains(&self, name: &str) -> bool {
let limiters = self.limiters.read().await;
limiters.contains_key(name)
}
pub async fn unregister(&self, name: &str) -> Result<(), FlowGuardError> {
let mut limiters = self.limiters.write().await;
if !limiters.contains_key(name) {
let error_msg = format!("限流器 '{}' 不存在", name);
warn!("{}", error_msg);
return Err(FlowGuardError::ConfigError(error_msg));
}
info!("注销自定义限流器: {}", name);
limiters.remove(name);
debug!("当前注册的限流器数量: {}", limiters.len());
Ok(())
}
#[allow(clippy::map_clone)]
pub async fn list(&self) -> Vec<String> {
let limiters = self.limiters.read().await;
limiters.keys().map(|k| k.clone()).collect()
}
pub async fn count(&self) -> usize {
let limiters = self.limiters.read().await;
limiters.len()
}
pub async fn clear(&self) {
let mut limiters = self.limiters.write().await;
info!("清空所有自定义限流器");
limiters.clear();
}
pub async fn allow(&self, name: &str, cost: u64) -> Result<bool, FlowGuardError> {
let limiters = self.limiters.read().await;
let limiter = limiters.get(name).ok_or_else(|| {
let error_msg = format!("限流器 '{}' 不存在", name);
error!("{}", error_msg);
FlowGuardError::ConfigError(error_msg)
})?;
debug!("使用限流器 '{}' 检查请求,成本: {}", name, cost);
limiter.allow(cost).await
}
pub async fn get_stats(&self, name: &str) -> Result<LimiterStats, FlowGuardError> {
let limiters = self.limiters.read().await;
let limiter = limiters.get(name).ok_or_else(|| {
let error_msg = format!("限流器 '{}' 不存在", name);
error!("{}", error_msg);
FlowGuardError::ConfigError(error_msg)
})?;
Ok(limiter.stats())
}
}
impl Default for CustomLimiterRegistry {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct LeakyBucketLimiter {
capacity: u64,
leak_rate: u64,
current: Arc<AtomicU64>,
queue: Arc<Mutex<VecDeque<Instant>>>,
stats: Arc<Mutex<LimiterStats>>,
}
impl LeakyBucketLimiter {
pub fn new(capacity: u64, leak_rate: u64) -> Self {
Self {
capacity,
leak_rate,
current: Arc::new(AtomicU64::new(0)),
queue: Arc::new(Mutex::new(VecDeque::new())),
stats: Arc::new(Mutex::new(LimiterStats::new())),
}
}
pub fn capacity(&self) -> u64 {
self.capacity
}
pub fn leak_rate(&self) -> u64 {
self.leak_rate
}
pub fn current(&self) -> u64 {
self.current.load(Ordering::SeqCst)
}
fn leak(&self) {
let mut queue = self.queue.lock().unwrap();
let now = Instant::now();
let leak_interval = Duration::from_secs(1).div_f64(self.leak_rate as f64);
while let Some(&front) = queue.front() {
if now.duration_since(front) >= leak_interval {
queue.pop_front();
self.current.fetch_sub(1, Ordering::SeqCst);
} else {
break;
}
}
}
}
#[async_trait]
impl CustomLimiter for LeakyBucketLimiter {
fn name(&self) -> &str {
"leaky_bucket"
}
async fn allow(&self, cost: u64) -> Result<bool, FlowGuardError> {
self.leak();
let current = self.current.load(Ordering::SeqCst);
if current + cost > self.capacity {
let mut stats = self.stats.lock().unwrap();
stats.total_requests += 1;
stats.rejected_requests += 1;
debug!(
"漏桶限流拒绝: 当前={}, 成本={}, 容量={}",
current, cost, self.capacity
);
return Ok(false);
}
self.current.fetch_add(cost, Ordering::SeqCst);
let mut queue = self.queue.lock().unwrap();
let now = Instant::now();
for _ in 0..cost {
queue.push_back(now);
}
let mut stats = self.stats.lock().unwrap();
stats.total_requests += 1;
stats.allowed_requests += 1;
debug!(
"漏桶限流允许: 当前={}, 成本={}, 容量={}",
current + cost,
cost,
self.capacity
);
Ok(true)
}
fn load_config(&mut self, config: Value) -> Result<(), FlowGuardError> {
let capacity = config["capacity"]
.as_u64()
.ok_or_else(|| FlowGuardError::ConfigError("缺少 capacity 配置".to_string()))?;
let leak_rate = config["leak_rate"]
.as_u64()
.ok_or_else(|| FlowGuardError::ConfigError("缺少 leak_rate 配置".to_string()))?;
if capacity == 0 {
return Err(FlowGuardError::ConfigError(
"capacity 必须大于 0".to_string(),
));
}
if leak_rate == 0 {
return Err(FlowGuardError::ConfigError(
"leak_rate 必须大于 0".to_string(),
));
}
self.capacity = capacity;
self.leak_rate = leak_rate;
info!(
"加载漏桶限流器配置: 容量={}, 流出速率={}",
self.capacity, self.leak_rate
);
Ok(())
}
fn stats(&self) -> LimiterStats {
self.stats.lock().unwrap().clone()
}
}
#[derive(Debug)]
pub struct TokenBucketLimiter {
capacity: u64,
refill_rate: u64,
tokens: Arc<AtomicU64>,
last_refill: Arc<AtomicU64>,
stats: Arc<Mutex<LimiterStats>>,
}
impl TokenBucketLimiter {
pub fn new(capacity: u64, refill_rate: u64) -> Self {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
Self {
capacity,
refill_rate,
tokens: Arc::new(AtomicU64::new(capacity)),
last_refill: Arc::new(AtomicU64::new(now)),
stats: Arc::new(Mutex::new(LimiterStats::new())),
}
}
pub fn capacity(&self) -> u64 {
self.capacity
}
pub fn refill_rate(&self) -> u64 {
self.refill_rate
}
pub fn tokens(&self) -> u64 {
self.tokens.load(Ordering::SeqCst)
}
fn refill_tokens(&self) {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
loop {
let last = self.last_refill.load(Ordering::SeqCst);
let elapsed_nanos = now.saturating_sub(last);
if elapsed_nanos < 1_000_000 {
break;
}
let elapsed_seconds = elapsed_nanos as f64 / 1_000_000_000.0;
let tokens_to_add = (elapsed_seconds * self.refill_rate as f64) as u64;
if tokens_to_add == 0 {
break;
}
if self
.last_refill
.compare_exchange(last, now, Ordering::SeqCst, Ordering::SeqCst)
.is_ok()
{
loop {
let current = self.tokens.load(Ordering::SeqCst);
let new_tokens = current.saturating_add(tokens_to_add).min(self.capacity);
if self
.tokens
.compare_exchange(current, new_tokens, Ordering::SeqCst, Ordering::SeqCst)
.is_ok()
{
break;
}
}
break;
}
}
}
}
#[async_trait]
impl CustomLimiter for TokenBucketLimiter {
fn name(&self) -> &str {
"token_bucket"
}
async fn allow(&self, cost: u64) -> Result<bool, FlowGuardError> {
self.refill_tokens();
loop {
let current = self.tokens.load(Ordering::SeqCst);
if current < cost {
let mut stats = self.stats.lock().unwrap();
stats.total_requests += 1;
stats.rejected_requests += 1;
debug!("令牌桶限流拒绝: 当前={}, 成本={}", current, cost);
return Ok(false);
}
if self
.tokens
.compare_exchange(current, current - cost, Ordering::SeqCst, Ordering::SeqCst)
.is_ok()
{
let mut stats = self.stats.lock().unwrap();
stats.total_requests += 1;
stats.allowed_requests += 1;
debug!("令牌桶限流允许: 当前={}, 成本={}", current - cost, cost);
return Ok(true);
}
}
}
fn load_config(&mut self, config: Value) -> Result<(), FlowGuardError> {
let capacity = config["capacity"]
.as_u64()
.ok_or_else(|| FlowGuardError::ConfigError("缺少 capacity 配置".to_string()))?;
let refill_rate = config["refill_rate"]
.as_u64()
.ok_or_else(|| FlowGuardError::ConfigError("缺少 refill_rate 配置".to_string()))?;
if capacity == 0 {
return Err(FlowGuardError::ConfigError(
"capacity 必须大于 0".to_string(),
));
}
if refill_rate == 0 {
return Err(FlowGuardError::ConfigError(
"refill_rate 必须大于 0".to_string(),
));
}
self.capacity = capacity;
self.refill_rate = refill_rate;
self.tokens.store(capacity, Ordering::SeqCst);
info!(
"加载令牌桶限流器配置: 容量={}, 补充速率={}",
self.capacity, self.refill_rate
);
Ok(())
}
fn stats(&self) -> LimiterStats {
self.stats.lock().unwrap().clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use tokio::time::sleep;
#[test]
fn test_limiter_stats_new() {
let stats = LimiterStats::new();
assert_eq!(stats.total_requests, 0);
assert_eq!(stats.allowed_requests, 0);
assert_eq!(stats.rejected_requests, 0);
}
#[test]
fn test_limiter_stats_rejection_rate() {
let mut stats = LimiterStats::new();
stats.total_requests = 100;
stats.rejected_requests = 20;
stats.allowed_requests = 80;
assert_eq!(stats.rejection_rate(), 0.2);
assert_eq!(stats.allow_rate(), 0.8);
}
#[test]
fn test_limiter_stats_zero_requests() {
let stats = LimiterStats::new();
assert_eq!(stats.rejection_rate(), 0.0);
assert_eq!(stats.allow_rate(), 0.0);
}
#[tokio::test]
async fn test_registry_new() {
let registry = CustomLimiterRegistry::new();
assert_eq!(registry.count().await, 0);
}
#[tokio::test]
async fn test_registry_register() {
let registry = CustomLimiterRegistry::new();
let limiter = LeakyBucketLimiter::new(100, 10);
assert!(registry
.register("leaky_bucket".to_string(), Box::new(limiter))
.await
.is_ok());
assert_eq!(registry.count().await, 1);
assert!(registry.contains("leaky_bucket").await);
}
#[tokio::test]
async fn test_registry_register_duplicate() {
let registry = CustomLimiterRegistry::new();
let limiter = LeakyBucketLimiter::new(100, 10);
assert!(registry
.register("leaky_bucket".to_string(), Box::new(limiter))
.await
.is_ok());
let result = registry
.register(
"leaky_bucket".to_string(),
Box::new(LeakyBucketLimiter::new(200, 20)),
)
.await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_registry_unregister() {
let registry = CustomLimiterRegistry::new();
let limiter = LeakyBucketLimiter::new(100, 10);
registry
.register("leaky_bucket".to_string(), Box::new(limiter))
.await
.unwrap();
assert!(registry.unregister("leaky_bucket").await.is_ok());
assert_eq!(registry.count().await, 0);
assert!(!registry.contains("leaky_bucket").await);
}
#[tokio::test]
async fn test_registry_unregister_nonexistent() {
let registry = CustomLimiterRegistry::new();
let result = registry.unregister("nonexistent").await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_registry_list() {
let registry = CustomLimiterRegistry::new();
registry
.register(
"limiter1".to_string(),
Box::new(LeakyBucketLimiter::new(100, 10)),
)
.await
.unwrap();
registry
.register(
"limiter2".to_string(),
Box::new(TokenBucketLimiter::new(100, 10)),
)
.await
.unwrap();
let list = registry.list().await;
assert_eq!(list.len(), 2);
assert!(list.contains(&"limiter1".to_string()));
assert!(list.contains(&"limiter2".to_string()));
}
#[tokio::test]
async fn test_registry_clear() {
let registry = CustomLimiterRegistry::new();
registry
.register(
"limiter1".to_string(),
Box::new(LeakyBucketLimiter::new(100, 10)),
)
.await
.unwrap();
registry.clear().await;
assert_eq!(registry.count().await, 0);
}
#[tokio::test]
async fn test_registry_allow() {
let registry = CustomLimiterRegistry::new();
let limiter = LeakyBucketLimiter::new(100, 10);
registry
.register("leaky_bucket".to_string(), Box::new(limiter))
.await
.unwrap();
let result = registry.allow("leaky_bucket", 1).await;
assert!(result.is_ok());
assert!(result.unwrap());
}
#[tokio::test]
async fn test_registry_allow_nonexistent() {
let registry = CustomLimiterRegistry::new();
let result = registry.allow("nonexistent", 1).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_registry_get_stats() {
let registry = CustomLimiterRegistry::new();
let limiter = LeakyBucketLimiter::new(100, 10);
registry
.register("leaky_bucket".to_string(), Box::new(limiter))
.await
.unwrap();
let result = registry.get_stats("leaky_bucket").await;
assert!(result.is_ok());
let stats = result.unwrap();
assert_eq!(stats.total_requests, 0);
}
#[tokio::test]
async fn test_leaky_bucket_new() {
let limiter = LeakyBucketLimiter::new(100, 10);
assert_eq!(limiter.name(), "leaky_bucket");
assert_eq!(limiter.capacity(), 100);
assert_eq!(limiter.leak_rate(), 10);
assert_eq!(limiter.current(), 0);
}
#[tokio::test]
async fn test_leaky_bucket_allow() {
let limiter = LeakyBucketLimiter::new(100, 10);
assert!(limiter.allow(1).await.unwrap());
assert_eq!(limiter.current(), 1);
}
#[tokio::test]
async fn test_leaky_bucket_exceeds_capacity() {
let limiter = LeakyBucketLimiter::new(10, 10);
assert!(limiter.allow(10).await.unwrap());
assert!(!limiter.allow(1).await.unwrap());
}
#[tokio::test]
async fn test_leaky_bucket_leak() {
let limiter = LeakyBucketLimiter::new(10, 100); limiter.allow(10).await.unwrap();
assert_eq!(limiter.current(), 10);
sleep(Duration::from_millis(20)).await; limiter.allow(0).await.unwrap(); assert!(limiter.current() < 10);
}
#[tokio::test]
async fn test_leaky_bucket_stats() {
let limiter = LeakyBucketLimiter::new(100, 10);
limiter.allow(1).await.unwrap();
let stats = limiter.stats();
assert_eq!(stats.total_requests, 1);
assert_eq!(stats.allowed_requests, 1);
assert_eq!(stats.rejected_requests, 0);
}
#[tokio::test]
async fn test_leaky_bucket_load_config() {
let mut limiter = LeakyBucketLimiter::new(100, 10);
let config = serde_json::json!({
"capacity": 200,
"leak_rate": 20
});
assert!(limiter.load_config(config).is_ok());
assert_eq!(limiter.capacity(), 200);
assert_eq!(limiter.leak_rate(), 20);
}
#[tokio::test]
async fn test_leaky_bucket_load_config_invalid() {
let mut limiter = LeakyBucketLimiter::new(100, 10);
let config = serde_json::json!({
"capacity": 0
});
let result = limiter.load_config(config);
assert!(result.is_err());
}
#[tokio::test]
async fn test_token_bucket_new() {
let limiter = TokenBucketLimiter::new(100, 10);
assert_eq!(limiter.name(), "token_bucket");
assert_eq!(limiter.capacity(), 100);
assert_eq!(limiter.refill_rate(), 10);
assert_eq!(limiter.tokens(), 100);
}
#[tokio::test]
async fn test_token_bucket_allow() {
let limiter = TokenBucketLimiter::new(100, 10);
assert!(limiter.allow(10).await.unwrap());
assert_eq!(limiter.tokens(), 90);
}
#[tokio::test]
async fn test_token_bucket_insufficient_tokens() {
let limiter = TokenBucketLimiter::new(10, 1);
assert!(limiter.allow(10).await.unwrap());
assert!(!limiter.allow(1).await.unwrap());
}
#[tokio::test]
async fn test_token_bucket_refill() {
let limiter = TokenBucketLimiter::new(10, 100); limiter.allow(10).await.unwrap();
assert_eq!(limiter.tokens(), 0);
sleep(Duration::from_millis(20)).await; limiter.allow(0).await.unwrap(); assert!(limiter.tokens() >= 1);
}
#[tokio::test]
async fn test_token_bucket_stats() {
let limiter = TokenBucketLimiter::new(100, 10);
limiter.allow(1).await.unwrap();
let stats = limiter.stats();
assert_eq!(stats.total_requests, 1);
assert_eq!(stats.allowed_requests, 1);
assert_eq!(stats.rejected_requests, 0);
}
#[tokio::test]
async fn test_token_bucket_load_config() {
let mut limiter = TokenBucketLimiter::new(100, 10);
let config = serde_json::json!({
"capacity": 200,
"refill_rate": 20
});
assert!(limiter.load_config(config).is_ok());
assert_eq!(limiter.capacity(), 200);
assert_eq!(limiter.refill_rate(), 20);
assert_eq!(limiter.tokens(), 200);
}
#[tokio::test]
async fn test_token_bucket_load_config_invalid() {
let mut limiter = TokenBucketLimiter::new(100, 10);
let config = serde_json::json!({
"capacity": 0
});
let result = limiter.load_config(config);
assert!(result.is_err());
}
#[tokio::test]
async fn test_registry_concurrent_register() {
let registry = Arc::new(CustomLimiterRegistry::new());
let mut handles = vec![];
for i in 0..10 {
let registry_clone = Arc::clone(®istry);
handles.push(tokio::spawn(async move {
let limiter = LeakyBucketLimiter::new(100 + i, 10);
registry_clone
.register(format!("limiter_{}", i), Box::new(limiter))
.await
}));
}
let mut success_count = 0;
for handle in handles {
if handle.await.unwrap().is_ok() {
success_count += 1;
}
}
assert_eq!(success_count, 10);
assert_eq!(registry.count().await, 10);
}
#[tokio::test]
async fn test_registry_concurrent_allow() {
let registry = Arc::new(CustomLimiterRegistry::new());
let limiter = LeakyBucketLimiter::new(100, 10);
registry
.register("leaky_bucket".to_string(), Box::new(limiter))
.await
.unwrap();
let mut handles = vec![];
for _ in 0..100 {
let registry_clone = Arc::clone(®istry);
handles.push(tokio::spawn(async move {
registry_clone.allow("leaky_bucket", 1).await
}));
}
let mut success_count = 0;
for handle in handles {
if let Ok(Ok(true)) = handle.await {
success_count += 1;
}
}
assert_eq!(success_count, 100);
}
#[tokio::test]
async fn test_leaky_bucket_concurrent() {
let limiter = Arc::new(LeakyBucketLimiter::new(100, 10));
let mut handles = vec![];
for _ in 0..10 {
let limiter_clone = Arc::clone(&limiter);
handles.push(tokio::spawn(async move {
for _ in 0..10 {
limiter_clone.allow(1).await.unwrap();
}
}));
}
for handle in handles {
handle.await.unwrap();
}
assert_eq!(limiter.current(), 100);
}
#[tokio::test]
async fn test_token_bucket_concurrent() {
let limiter = Arc::new(TokenBucketLimiter::new(100, 10));
let mut handles = vec![];
for _ in 0..10 {
let limiter_clone = Arc::clone(&limiter);
handles.push(tokio::spawn(async move {
for _ in 0..10 {
limiter_clone.allow(1).await.unwrap();
}
}));
}
for handle in handles {
handle.await.unwrap();
}
assert_eq!(limiter.tokens(), 0);
}
}