### Implement Custom Rate Limiting Middleware in Rust Source: https://context7.com/boinkor-net/governor/llms.txt Extend RateLimitingMiddleware to add side effects like metrics collection. This example counts allowed and denied requests. ```rust use governor::{ Quota, RateLimiter, middleware::{RateLimitingMiddleware, StateSnapshot}, clock::Reference, }; use nonzero_ext::nonzero; use std::sync::atomic::{AtomicU64, Ordering}; // Custom middleware that counts allowed and denied requests static ALLOWED_COUNT: AtomicU64 = AtomicU64::new(0); static DENIED_COUNT: AtomicU64 = AtomicU64::new(0); #[derive(Debug)] struct MetricsMiddleware; impl RateLimitingMiddleware

for MetricsMiddleware { type PositiveOutcome = u64; // Return the count of allowed requests type NegativeOutcome = (); // Minimal error type fn allow(_key: &K, _state: impl Into) -> Self::PositiveOutcome { ALLOWED_COUNT.fetch_add(1, Ordering::Relaxed) + 1 } fn disallow(_key: &K, _state: impl Into, _start: P) -> Self::NegativeOutcome { DENIED_COUNT.fetch_add(1, Ordering::Relaxed); } } fn main() { let limiter = RateLimiter::direct(Quota::per_second(nonzero!(2u32))) .with_middleware::(); // Returns Ok(count) on success match limiter.check() { Ok(count) => println!("Request #{} allowed", count), Err(()) => println!("Request denied"), } match limiter.check() { Ok(count) => println!("Request #{} allowed", count), Err(()) => println!("Request denied"), } // Third request exceeds limit match limiter.check() { Ok(count) => println!("Request #{} allowed", count), Err(()) => println!("Request denied"), } println!("Total allowed: {}", ALLOWED_COUNT.load(Ordering::Relaxed)); println!("Total denied: {}", DENIED_COUNT.load(Ordering::Relaxed)); } ``` -------------------------------- ### Async Rate Limiting with Direct Limiter Source: https://context7.com/boinkor-net/governor/llms.txt Use `until_ready` to block asynchronously until the rate limiter permits the next request. This is useful for direct rate limiting where no specific key is involved. ```rust use governor::{Quota, RateLimiter, Jitter}; use nonzero_ext::nonzero; use std::time::Duration; #[tokio::main] async fn main() { // Direct rate limiter - async waiting let limiter = RateLimiter::direct(Quota::per_second(nonzero!(2u32))); for i in 0..5 { // Blocks until rate limiter allows the request limiter.until_ready().await; println!("Request {} sent at {:?}", i, std::time::Instant::now()); } // With jitter to prevent thundering herd let jitter = Jitter::up_to(Duration::from_millis(100)); limiter.until_ready_with_jitter(jitter).await; // Wait for multiple cells use std::num::NonZeroU32; let n = NonZeroU32::new(3).unwrap(); match limiter.until_n_ready(n).await { Ok(()) => println!("3 cells acquired"), Err(insufficient) => println!("Capacity error: {}", insufficient), } } ``` -------------------------------- ### Create and Use a Keyed Rate Limiter Source: https://context7.com/boinkor-net/governor/llms.txt Implement per-key rate limiting using `RateLimiter::keyed`. This is suitable for scenarios like per-user or per-API-key limits. Use `check_key` and `check_key_n` for individual or batch checks on specific keys. Stale keys can be managed with `retain_recent` and `shrink_to_fit`. ```rust use governor::{Quota, RateLimiter}; use nonzero_ext::nonzero; // Create a keyed rate limiter: 10 requests per second per key let limiter = RateLimiter::keyed(Quota::per_second(nonzero!(10u32))); // Rate limit by user ID let user_id_1 = "user_123"; let user_id_2 = "user_456"; // Each key has independent rate limiting assert!(limiter.check_key(&user_id_1).is_ok()); assert!(limiter.check_key(&user_id_2).is_ok()); // Different key, independent limit // Check multiple cells for a specific key use std::num::NonZeroU32; let n = NonZeroU32::new(5).unwrap(); match limiter.check_key_n(&user_id_1, n) { Ok(Ok(())) => println!("5 cells allowed for {}", user_id_1), Ok(Err(not_until)) => println!("Rate limited for {}", user_id_1), Err(insufficient) => println!("Batch exceeds capacity"), } // Housekeeping: remove stale keys to free memory limiter.retain_recent(); limiter.shrink_to_fit(); println!("Active keys: {}", limiter.len()); ``` -------------------------------- ### Async Keyed Rate Limiting Source: https://context7.com/boinkor-net/governor/llms.txt Utilize `until_key_ready` for asynchronous rate limiting based on specific keys, such as API keys. This allows different clients or resources to have independent rate limits. ```rust use governor::{Quota, RateLimiter, Jitter}; use nonzero_ext::nonzero; use std::time::Duration; #[tokio::main] async fn main() { // Keyed rate limiter - async waiting let keyed_limiter = RateLimiter::keyed(Quota::per_second(nonzero!(5u32))); let api_key = "client_abc"; keyed_limiter.until_key_ready(&api_key).await; println!("Request for {} allowed", api_key); // With jitter for keyed limiter let jitter = Jitter::up_to(Duration::from_millis(100)); keyed_limiter.until_key_ready_with_jitter(&api_key, jitter).await; } ``` -------------------------------- ### Create and Use a Direct Rate Limiter Source: https://context7.com/boinkor-net/governor/llms.txt Instantiate a direct rate limiter for global rate limiting scenarios. Use the `check` method to determine if a request is allowed, or `check_n` for batch operations. The `check` method returns an error with information on when the request would be permitted. ```rust use governor::{Quota, RateLimiter}; use nonzero_ext::nonzero; // Create a rate limiter allowing 5 requests per second let limiter = RateLimiter::direct(Quota::per_second(nonzero!(5u32))); // Check if a request is allowed match limiter.check() { Ok(()) => println!("Request allowed"), Err(not_until) => { println!("Rate limited until {:?}", not_until.earliest_possible()); println!("Wait time: {:?}", not_until.wait_time_from(std::time::Instant::now())); } } // Check multiple cells at once (e.g., for batch operations) use std::num::NonZeroU32; let n = NonZeroU32::new(3).unwrap(); match limiter.check_n(n) { Ok(Ok(())) => println!("All 3 cells allowed"), Ok(Err(not_until)) => println!("Not enough capacity, try again later"), Err(insufficient) => println!("Batch size {} exceeds burst capacity", insufficient.0), } ``` -------------------------------- ### Use StateInformationMiddleware for Rate Limit Headers Source: https://context7.com/boinkor-net/governor/llms.txt StateInformationMiddleware provides snapshots of the rate limiter state. It is commonly used to generate X-RateLimit headers for HTTP responses. ```rust use governor::{Quota, RateLimiter, middleware::StateInformationMiddleware}; use nonzero_ext::nonzero; // Create rate limiter with state information middleware let limiter = RateLimiter::direct(Quota::per_second(nonzero!(10u32))) .with_middleware::(); // Check returns StateSnapshot on success match limiter.check() { Ok(snapshot) => { println!("Remaining burst capacity: {}", snapshot.remaining_burst_capacity()); let quota = snapshot.quota(); println!("Burst size: {}", quota.burst_size()); println!("Replenish interval: {:?}", quota.replenish_interval()); } Err(not_until) => { println!("Rate limited!"); println!("Quota burst size: {}", not_until.quota().burst_size()); println!("Earliest retry: {:?}", not_until.earliest_possible()); } } // Example: Building HTTP rate limit headers fn build_rate_limit_headers(limiter: &RateLimiter< governor::state::NotKeyed, governor::state::InMemoryState, governor::clock::DefaultClock, StateInformationMiddleware, >) -> Vec<(&'static str, String)> { match limiter.check() { Ok(snapshot) => { vec![ ("X-RateLimit-Remaining", snapshot.remaining_burst_capacity().to_string()), ("X-RateLimit-Limit", snapshot.quota().burst_size().to_string()), ] } Err(not_until) => { let wait_secs = not_until.wait_time_from(std::time::Instant::now()).as_secs(); vec![ ("X-RateLimit-Remaining", "0".to_string()), ("Retry-After", wait_secs.to_string()), ] } } } ``` -------------------------------- ### Thread-Safe Rate Limiter Usage with Arc in Rust Source: https://context7.com/boinkor-net/governor/llms.txt Demonstrates how to share a RateLimiter across multiple threads using Arc for thread-safe access in concurrent applications. ```rust use governor::{Quota, RateLimiter}; use nonzero_ext::nonzero; use std::sync::Arc; use std::thread; fn main() { // Wrap rate limiter in Arc for thread sharing let limiter = Arc::new(RateLimiter::direct(Quota::per_second(nonzero!(100u32)))); let mut handles = vec![]; // Spawn multiple threads sharing the same rate limiter for thread_id in 0..4 { let limiter = Arc::clone(&limiter); let handle = thread::spawn(move || { let mut allowed = 0; let mut denied = 0; for _ in 0..50 { match limiter.check() { Ok(()) => allowed += 1, Err(_) => denied += 1, } } println!("Thread {}: allowed={}, denied={}", thread_id, allowed, denied); (allowed, denied) }); handles.push(handle); } // Collect results let mut total_allowed = 0; let mut total_denied = 0; for handle in handles { let (allowed, denied) = handle.join().unwrap(); total_allowed += allowed; total_denied += denied; } println!("Total: allowed={}, denied={}", total_allowed, total_denied); // With 100/sec quota and 200 total requests in quick succession, // approximately 100 will be allowed (burst capacity) } ``` -------------------------------- ### Rate-Limited Sinks with SinkRateLimitExt Source: https://context7.com/boinkor-net/governor/llms.txt Wrap any `Sink` with `ratelimit_sink` to control the rate at which items can be sent. Use `ratelimit_sink_with_jitter` to add jitter for more distributed sending. ```rust use governor::{Quota, RateLimiter, Jitter, prelude::*}; use nonzero_ext::nonzero; use futures_util::sink::{self, SinkExt}; use std::time::Duration; #[tokio::main] async fn main() -> Result<(), Box> { let limiter = RateLimiter::direct(Quota::per_second(nonzero!(2u32))); // Create a drain sink (discards all items) let drain = sink::drain(); // Rate-limit the sink let mut limited_sink = drain.ratelimit_sink(&limiter); // Send items at limited rate for i in 0..5 { limited_sink.send(i).await?; println!("Sent item {} at {:?}", i, std::time::Instant::now()); } // With jitter let drain2 = sink::drain(); let jitter = Jitter::new(Duration::from_millis(10), Duration::from_millis(50)); let mut jittered_sink = drain2.ratelimit_sink_with_jitter(&limiter, jitter); jittered_sink.send("message").await?; // Access underlying sink let drain3 = sink::drain(); let mut limited = drain3.ratelimit_sink(&limiter); limited.get_mut().send(42).await?; Ok(()) } ``` -------------------------------- ### Implement Rate Limiting with Type Aliases Source: https://context7.com/boinkor-net/governor/llms.txt Utilize DefaultDirectRateLimiter and DefaultKeyedRateLimiter to simplify struct definitions for direct and per-user rate limiting. ```rust use governor::{DefaultDirectRateLimiter, DefaultKeyedRateLimiter, Quota, RateLimiter}; use nonzero_ext::nonzero; // Use type aliases for cleaner struct definitions struct ApiClient { rate_limiter: DefaultDirectRateLimiter, } impl ApiClient { fn new(requests_per_second: u32) -> Self { let quota = Quota::per_second( std::num::NonZeroU32::new(requests_per_second).unwrap() ); Self { rate_limiter: RateLimiter::direct(quota), } } fn make_request(&self) -> Result<(), &'static str> { match self.rate_limiter.check() { Ok(()) => { // Proceed with API call Ok(()) } Err(_) => Err("Rate limited"), } } } // Keyed rate limiter for per-user limits struct MultiTenantService { rate_limiter: DefaultKeyedRateLimiter, } impl MultiTenantService { fn new() -> Self { Self { rate_limiter: RateLimiter::keyed(Quota::per_minute(nonzero!(60u32))), } } fn handle_request(&self, user_id: &str) -> Result<(), &'static str> { match self.rate_limiter.check_key(&user_id.to_string()) { Ok(()) => Ok(()), Err(_) => Err("User rate limited"), } } } fn main() { let client = ApiClient::new(10); let _ = client.make_request(); let service = MultiTenantService::new(); let _ = service.handle_request("user_123"); } ``` -------------------------------- ### Rate-Limited Streams with StreamRateLimitExt Source: https://context7.com/boinkor-net/governor/llms.txt Wrap any `Stream` with `ratelimit_stream` to control the rate at which items are yielded. Use `ratelimit_stream_with_jitter` to add jitter and prevent synchronized bursts. ```rust use governor::{Quota, RateLimiter, Jitter, prelude::*}; use nonzero_ext::nonzero; use futures_util::{stream, StreamExt}; use std::time::Duration; #[tokio::main] async fn main() { let limiter = RateLimiter::direct(Quota::per_second(nonzero!(3u32))); // Create a stream of items let items = stream::iter(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); // Rate-limit the stream to 3 items per second let mut limited_stream = items.ratelimit_stream(&limiter); while let Some(item) = limited_stream.next().await { println!("Received item: {} at {:?}", item, std::time::Instant::now()); } // With jitter to prevent synchronized bursts let items2 = stream::iter(vec!["a", "b", "c"]); let jitter = Jitter::up_to(Duration::from_millis(50)); let mut jittered_stream = items2.ratelimit_stream_with_jitter(&limiter, jitter); while let Some(item) = jittered_stream.next().await { println!("Received: {}", item); } // Access underlying stream let items3 = stream::iter(vec![1, 2, 3]); let limited = items3.ratelimit_stream(&limiter); let (inner_stream, buffered_item) = limited.into_inner(); } ``` -------------------------------- ### Define Rate Limits with Quota Source: https://context7.com/boinkor-net/governor/llms.txt Construct Quota objects to define rate limiting parameters like elements per period and burst capacity. Convenient methods exist for common time periods, and custom periods with burst allowances can also be configured. ```rust use governor::Quota; use nonzero_ext::nonzero; use std::time::Duration; // Allow 50 requests per second with burst capacity of 50 let quota_per_second = Quota::per_second(nonzero!(50u32)); assert_eq!(quota_per_second.burst_size().get(), 50); assert_eq!(quota_per_second.replenish_interval(), Duration::from_millis(20)); // Allow 100 requests per minute with burst capacity of 100 let quota_per_minute = Quota::per_minute(nonzero!(100u32)); // Allow 1000 requests per hour with burst capacity of 1000 let quota_per_hour = Quota::per_hour(nonzero!(1000u32)); // Custom: 10 requests per second with burst capacity of 50 let quota_with_burst = Quota::per_second(nonzero!(10u32)).allow_burst(nonzero!(50u32)); assert_eq!(quota_with_burst.burst_size().get(), 50); // Custom period: 1 request per day with burst of 10 let quota_custom = Quota::with_period(Duration::from_secs(60 * 60 * 24)) .unwrap() .allow_burst(nonzero!(10u32)); ``` -------------------------------- ### Test Rate Limiting with FakeRelativeClock Source: https://context7.com/boinkor-net/governor/llms.txt FakeRelativeClock allows for deterministic testing of rate-limiting logic by manually advancing time. Clones of the clock share the same internal time state. ```rust use governor::{Quota, RateLimiter, clock::FakeRelativeClock}; use nonzero_ext::nonzero; use std::time::Duration; // Create a fake clock for testing let clock = FakeRelativeClock::default(); // Create rate limiter with fake clock let limiter = RateLimiter::direct_with_clock( Quota::per_second(nonzero!(2u32)), clock.clone(), ); // First two requests succeed (burst capacity = 2) assert!(limiter.check().is_ok()); assert!(limiter.check().is_ok()); // Third request is rate limited assert!(limiter.check().is_err()); // Advance time by 500ms (replenishes 1 cell) clock.advance(Duration::from_millis(500)); assert!(limiter.check().is_ok()); assert!(limiter.check().is_err()); // Advance time by 1 second (fully replenishes) clock.advance(Duration::from_secs(1)); assert!(limiter.check().is_ok()); assert!(limiter.check().is_ok()); // Clones share the same time let clock2 = clock.clone(); clock.advance(Duration::from_secs(1)); assert_eq!(clock, clock2); // Both see the same time ``` -------------------------------- ### Implement Randomized Jitter in Rust Source: https://context7.com/boinkor-net/governor/llms.txt Use Jitter to add randomized delays to rate-limited operations. This helps prevent thundering herd issues when multiple tasks wait on the same limiter. ```rust use governor::Jitter; use std::time::Duration; // Jitter up to 100ms (0 to 100ms random delay) let jitter_up_to = Jitter::up_to(Duration::from_millis(100)); // Jitter with minimum: wait at least 50ms, up to 150ms total let jitter_with_min = Jitter::new( Duration::from_millis(50), // minimum wait Duration::from_millis(100), // additional random interval ); // Apply jitter to a duration let base_wait = Duration::from_secs(1); let jittered_wait = jitter_with_min + base_wait; assert!(jittered_wait >= Duration::from_millis(1050)); assert!(jittered_wait < Duration::from_millis(1150)); // Apply jitter to an Instant let now = std::time::Instant::now(); let jittered_instant = jitter_up_to + now; assert!(jittered_instant >= now); assert!(jittered_instant < now + Duration::from_millis(100)); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.