Rate Limiting в Rust: эффективное управление нагрузкой
Rate limiting (ограничение частоты запросов) — ключевой механизм защиты и оптимизации веб-сервисов, API и сетевых приложений. В экосистеме Rust существует несколько зрелых библиотек, реализующих различные алгоритмы: от классического токен-бакета до современных адаптивных методов. В этой статье мы разберём три основные библиотеки: governor, leaky-bucket и ratelimit, с примерами кода и рекомендациями по выбору.
1. Библиотека governor — золотой стандарт
governor — самая популярная и гибкая библиотека для rate limiting. Она реализует алгоритм Generic Cell Rate Algorithm (GCRA), который обеспечивает точное соблюдение лимитов с минимальной задержкой. Поддерживает синхронный и асинхронный режимы, middleware для Actix-web, Axum и Rocket.
Базовый пример с governor
use governor::{DefaultKeyedRateLimiter, Quota, RateLimiter};
use std::num::NonZeroU32;
use std::time::Duration;
fn main() {
// Разрешаем 5 запросов в секунду
let quota = Quota::per_second(NonZeroU32::new(5).unwrap());
let limiter = RateLimiter::direct(quota);
for i in 0..10 {
match limiter.check() {
Ok(_) => println!("Запрос {} разрешён", i),
Err(_) => println!("Запрос {} отклонён (rate limit)", i),
}
std::thread::sleep(Duration::from_millis(100));
}
}
Асинхронный вариант с ожиданием
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!("Обработан запрос {}", i);
sleep(Duration::from_millis(300)).await;
}
}
Ключевые возможности governor:
- Поддержка ключевых лимитов (per-IP, per-user) через
KeyedRateLimiter - Интеграция с
towermiddleware для HTTP-серверов - Настраиваемые стратегии: ожидание, отклонение, адаптивное замедление
- Минимальное потребление памяти (один атомарный счётчик на лимитер)
2. Библиотека leaky-bucket — простота и надёжность
leaky-bucket реализует классический алгоритм «дырявого ведра». Он идеален для сглаживания пиковых нагрузок, когда нужно гарантировать равномерный поток запросов. Библиотека легковесна и не требует внешних зависимостей.
Пример с leaky-bucket
use leaky_bucket::RateLimiter;
use std::time::Duration;
#[tokio::main]
async fn main() {
let limiter = RateLimiter::builder()
.initial(10) // начальный запас токенов
.interval(Duration::from_secs(1)) // интервал пополнения
.refill(5) // сколько токенов добавлять за интервал
.max(20) // максимальное количество токенов
.build();
for i in 0..15 {
limiter.acquire_one().await;
println!("Запрос {} выполнен", i);
}
}
Особенности leaky-bucket:
- Поддержка асинхронного и блокирующего API
- Возможность настройки «burst»-режима (кратковременные всплески)
- Автоматическое освобождение токенов при простое
3. Библиотека ratelimit — минималистичный подход
ratelimit — самая простая библиотека, реализующая скользящее окно с фиксированными интервалами. Подходит для простых сценариев, где не нужна высокая точность.
Пример с 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() {
println!("Запрос {} разрешён", i);
} else {
println!("Запрос {} отклонён", i);
}
std::thread::sleep(Duration::from_millis(50));
}
}
Когда выбирать ratelimit:
- Прототипирование и MVP
- Сценарии с низкой точностью (
Также в библиотеке
Очереди задач в Swift: GCD, OperationQueue и Swift Concurrency
Реактивное программирование в Kotlin: библиотека RxKotlin и корутины
Работа с CSV в C++: библиотеки, парсинг и запись данных
regex для Rust: полное руководство с примерами
Генерация PDF в Java: обзор библиотек iText, Apache PDFBox, OpenPDF