Rate Limiting em Rust: gerenciamento eficiente de carga
Rate limiting (limitação de frequência de requisições) é um mecanismo essencial para proteger e otimizar serviços web, APIs e aplicações de rede. No ecossistema Rust, existem várias bibliotecas maduras que implementam diferentes algoritmos: desde o clássico token bucket até métodos adaptativos modernos. Neste artigo, vamos explorar três bibliotecas principais: governor, leaky-bucket e ratelimit, com exemplos de código e recomendações de uso.
1. Biblioteca governor — o padrão ouro
governor é a biblioteca mais popular e flexível para rate limiting. Ela implementa o algoritmo Generic Cell Rate Algorithm (GCRA), que garante limites precisos com latência mínima. Suporta modos síncrono e assíncrono, além de middleware para Actix-web, Axum e Rocket.
Exemplo básico com governor
use governor::{DefaultKeyedRateLimiter, Quota, RateLimiter};
use std::num::NonZeroU32;
use std::time::Duration;
fn main() {
// Permite 5 requisições por segundo
let quota = Quota::per_second(NonZeroU32::new(5).unwrap());
let limiter = RateLimiter::direct(quota);
for i in 0..10 {
match limiter.check() {
Ok(_) => println!("Requisição {} permitida", i),
Err(_) => println!("Requisição {} negada (rate limit)", i),
}
std::thread::sleep(Duration::from_millis(100));
}
}
Versão assíncrona com espera
use governor::{Quota, RateLimiter};
use std::num::NonZeroU32;
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
let limiter = RateLimiter::direct(Quota::per_second(
NonZeroU32::new(2).unwrap(),
));
for i in 0..5 {
limiter.until_ready().await;
println("Requisição {} processada", i);
sleep(Duration::from_millis(300)).await;
}
}
Principais recursos do governor:
- Suporte a limites por chave (por IP, por usuário) via
KeyedRateLimiter - Integração com middleware
towerpara servidores HTTP - Estratégias configuráveis: esperar, rejeitar, desacelerar adaptativamente
- Consumo mínimo de memória (um contador atômico por limitador)
2. Biblioteca leaky-bucket — simplicidade e confiabilidade
leaky-bucket implementa o clássico algoritmo do "balde furado". É ideal para suavizar picos de carga, garantindo um fluxo uniforme de requisições. A biblioteca é leve e não requer dependências externas.
Exemplo com leaky-bucket
use leaky_bucket::RateLimiter;
use std::time::Duration;
#[tokio::main]
async fn main() {
let limiter = RateLimiter::builder()
.initial(10) // saldo inicial de tokens
.interval(Duration::from_secs(1)) // intervalo de recarga
.refill(5) // quantos tokens adicionar por intervalo
.max(20) // número máximo de tokens
.build();
for i in 0..15 {
limiter.acquire_one().await;
println!("Requisição {} concluída", i);
}
}
Características do leaky-bucket:
- Suporte a API assíncrona e bloqueante
- Possibilidade de configurar modo "burst" (picos curtos de requisições)
- Liberação automática de tokens durante períodos de inatividade
3. Biblioteca ratelimit — abordagem minimalista
ratelimit é a biblioteca mais simples, implementando uma janela deslizante com intervalos fixos. Adequada para cenários simples onde não é necessária alta precisão.
Exemplo com ratelimit
use ratelimit::Ratelimiter;
use std::time::Duration;
fn main() {
let mut limiter = Ratelimiter::builder(10, Duration::from_secs(1))
.build()
.unwrap();
for i in 0..20 {
if limiter.try_wait() {