Теория
Зачем нужен rate limiting
- защита от brute force и credential stuffing;
- ограничение API abuse и квоты;
- смягчение L7 flood;
- контроль стоимости дорогих endpoints.
Алгоритмы
Token Bucket
Ёмкость b, refill rate r. Запрос тратит токен. При накопленных токенах допускает controlled burst до b. RFC 2697 описывает srTCM (single rate three color marker), близкая модель.
Leaky Bucket
Запросы вытекают с фиксированной скоростью; избыток — drop или delay. NGINX limit_req реализован как leaky bucket: nodelay отдаёт burst немедленно, без nodelay — задерживает.
Fixed Window
Счётчик в фиксированном интервале. Дёшево; boundary burst на стыке окон даёт до 2× rate.
Sliding Window
Аппроксимация: count = prev × (1 − elapsed/window) + current. Меньше boundary-артефактов, дороже по состоянию.
Sliding Log
Хранит timestamp каждого запроса. Точнее всех; O(N) память.
Distributed Rate Limiting
Проблемы распределённого лимитера:
race conditionsпри конкурентных обновлениях;consistencyмежду узлами;replication lag(особенно при async replication);- hot keys и неравномерная нагрузка.
Рабочие паттерны:
- sharded counters;
- consistent hashing по limit key;
- local + global hybrid (локальный токен-бакет + глобальный decision service).
Ключевые параметры
limit key: IP / user / API token / route + token.burst: сколько допускаем кратковременно сверх steady rate.delay vs drop: деградировать мягко или жёстко отбрасывать.sliding granularity: точность окна vs стоимость вычисления.
Что нужно уметь объяснить
Почему distributed rate limiting сложнее локального?
Локальный лимитер видит только локальный поток.
Distributed лимитер должен синхронно (или квазисинхронно) согласовать состояние между узлами под гонками и задержками репликации.
Почему ключ лимита важнее «самого алгоритма»?
Плохой key (IP за NAT, общий service token) даёт или массовые FP, или бесполезно слабую защиту.
Когда лучше delay, а когда drop?
delay: когда важно мягко сгладить всплеск.drop/429: когда нужно быстро освободить ресурсы origin.
Практика
1. Базовый лимит в NGINX
limit_req_zone $binary_remote_addr zone=api_limit:20m rate=20r/s;
location /api/ {
limit_req zone=api_limit burst=40 nodelay;
proxy_pass http://backend;
}
2. Проверьте поведение на burst
wrk -t8 -c400 -d30s https://example.com/api/
Смотрите:
- долю
429/503; - latency p95/p99;
- CPU на edge/origin.
3. Разделите ключи лимита
Сравните:
- только
IP; API token;- composite key (
route + token).
4. Для distributed-модели разделите local/global слой
- local limiter на edge (быстрый fail-fast);
- global limiter для account-wide quota/policy.
HTTP-протокол
- статус:
429 Too Many Requests(RFC 6585); Retry-After(RFC 9110 §10.2.3) — секунды или HTTP-date;RateLimit/RateLimit-Policyheaders — RFC 9239;X-RateLimit-*остаются распространёнными, но нестандартными vendor headers.