rate limits

Когда вы запускаете первый пет-проект, вопрос ограничений часто вообще не стоит. Но как только сервис выходит в продакшн и на него налетают либо реальные пользователи, либо криво написанные скрипты-парсеры, либо злоумышленники с DDoS-атакой, выясняется неприятная вещь: ресурсы сервера конечны. CPU сгорает, база данных уходит в «ступор» из-за количества соединений, а память заканчивается быстрее, чем вы успеваете открыть дашборд мониторинга.
Здесь на сцену выходит Rate Limiting (ограничение частоты запросов). Это не просто «заглушка», а критически важный механизм обеспечения отказоустойчивости (resilience) и безопасности системы.
Зачем это нужно на самом деле?
Многие думают, что Rate Limit нужен только для защиты от хакеров. Это заблуждение. Вот основные причины, почему без него нельзя обходиться:
- Защита от «шумных соседей» (Noisy Neighbor Problem). В многопользовательских системах один клиент может забить весь канал запросами, из-за чего остальные пользователи будут видеть 504 Gateway Timeout.
- Предотвращение каскадных сбоев. Если один из микросервисов начинает тормозить, а вызывающая сторона продолжает слать запросы с огромной скоростью, очередь накапливается, и в итоге падает вся цепочка сервисов.
- Экономия денег. Если вы используете платные внешние API (например, OpenAI или Google Maps), бесконтрольный поток запросов может обнулить ваш бюджет за несколько часов.
- Борьба с брутфорсом и парсингом. Ограничение попыток входа или частоты скачивания страниц делает автоматический перебор паролей или кражу контента экономически невыгодными.
Популярные алгоритмы реализации
Выбор алгоритма зависит от того, насколько строгими должны быть ограничения и сколько ресурсов вы готовы потратить на их отслеживание.
1. Fixed Window Counter (Счетчик в фиксированном окне)
Самый простой вариант. Мы определяем временной отрезок (например, 1 минута) и лимит (например, 100 запросов).
-
- Как работает: Создается запись в кэше с ключом
user_id:timestamp. С каждым запросом счетчик инкрементируется. Если он превышает 100 — возвращаем ошибку.
- Как работает: Создается запись в кэше с ключом
-
- Минус: «Проблема границы». Если пользователь отправит 100 запросов в последние 2 секунды первой минуты и еще 100 в первые 2 секунды следующей, система пропустит 200 запросов за очень короткий промежуток времени.
2. Sliding Window Log (Лог скользящего окна)
Более точный, но ресурсозатратный метод.
-
- Как работает: Для каждого пользователя хранится список меток времени (timestamps) всех его запросов. При новом запросе удаляются все метки, которые старше текущего окна, и проверяется размер оставшегося списка.
-
- Минус: Огромный расход памяти, если лимиты высокие. Хранить тысячи временных меток для каждого пользователя в Redis — плохая идея.
3. Token Bucket (Корзина с токенами)
Классика, используемая в Amazon и многих других гигантах.
-
- Как работает: У пользователя есть «корзина», куда с определенной скоростью (например, 2 токена в секунду) падают токены. Максимальный размер корзины ограничен. Каждый запрос «съедает» один токен. Если корзина пуста — запрос отклоняется.
-
- Плюс: Позволяет обрабатывать кратковременные всплески трафика (bursts), пока в корзине есть запас токенов.
4. Leaky Bucket (Дырявое ведро)
Похоже на предыдущий, но с одним важным отличием: запросы выходят из системы с постоянной скоростью.
-
- Как работает: Запросы попадают в очередь (ведро). Независимо от того, как быстро они туда залетают, сервер обрабатывает их строго по одному в секунду. Если ведро переполнилось — новые запросы выбрасываются.
-
- Плюс: Идеально для сглаживания трафика и защиты бэкенда от резких скачков.
Где именно внедрять ограничения?
В зависимости от архитектуры, Rate Limit можно поставить на разных уровнях:
- Client-side (Клиент). Сделать ограничение в самом приложении. Это полезно для UX (чтобы пользователь не кликал по кнопке «Отправить» десять раз в секунду), но абсолютно бесполезно для безопасности, так как API-запрос можно отправить через
curlили Postman. - API Gateway / Reverse Proxy. Самое правильное место. Nginx, Kong, Traefik или AWS API Gateway умеют ограничивать трафик еще до того, как запрос дойдет до вашего приложения. Это экономит ресурсы вашего приложения.
- Application Level (Код приложения). Реализуется через Middleware. Нужно, если логика ограничения сложная (например, лимиты зависят от подписки пользователя в базе данных или от сложности самого запроса).
Практические советы по внедрению
Если вы решили внедрить Rate Limiting, не забудьте про следующие моменты, чтобы не разозлить пользователей и коллег:
Информативные заголовки. Не просто отдавайте 429 Too Many Requests, а добавьте в ответ HTTP-заголовки:
-
X-RateLimit-Limit: Общий лимит.
-
X-RateLimit-Remaining: Сколько запросов осталось.
-
Retry-After: Через сколько секунд можно повторить попытку.
Использование Redis. Не храните счетчики в памяти самого приложения (in-memory), если у вас больше одного экземпляра сервиса (горизонтальное масштабирование). Используйте Redis — он быстрый, поддерживает атомарные операции (INCR) и имеет встроенный механизм TTL (время жизни ключа).
Гибкость лимитов. Разделяйте лимиты для разных типов пользователей.
-
- Анонимы: 10 запросов в минуту.
-
- Зарегистрированные пользователи: 100 запросов в минуту.
-
- Админы/Партнеры: Безлимит или очень высокий порог.
White-listing. Оставьте возможность добавлять IP-адреса в белый список (например, для внутренних сервисов мониторинга или доверенных партнеров), чтобы случайно не заблокировать самого себя.
Резюме
Rate Limiting — это не про ограничение возможностей пользователя, а про обеспечение стабильности системы. Начинать лучше с простого Fixed Window на уровне Nginx, но по мере роста продукта переходить к более гибким схемам вроде Token Bucket с хранением состояния в распределенном кэше. Помните: лучше вернуть пользователю ошибку 429, чем позволить всей системе упасть в бесконечный ребут из-за перегрузки.

