Стратегии ограничения частоты запросов для веб-API
Rate limiting контролирует, сколько запросов клиент может сделать к API документацию за определённый период. Защищает инфраструктуру от злоупотреблений, обеспечивает справедливое использование и предотвращает монополизацию ресурсов.
Зачем нужен rate limiting
- Защита от DDoS: ограничивает влияние объёмных атак
- Справедливость: один пользователь не истощает ресурсы для других
- Контроль затрат: ограничение дорогих операций (БД, сторонние API)
- Монетизация API: тарифы лимиты (free, pro, business)
- Стабильность: защита от случайных всплесков (сломанные retry-циклы, штормы краулеров)
Алгоритмы
Fixed Window
Подсчёт запросов в фиксированных окнах (например, за минуту). Сброс счётчика в начале окна.
- Плюс: простая реализация, мало памяти
- Минус: проблема всплеска — 100 запросов в 0:59 и 100 в 1:00 = двойной лимит
Sliding Window Log
Хранит таймстамп каждого запроса. Считает за последние N секунд.
- Плюс: точный, без всплесков
- Минус: много памяти
Sliding Window Counter
Гибрид: фиксированные окна с пропорциональным весом предыдущего. Если прошло 30% текущего окна — считаем 70% предыдущего + 100% текущего.
- Плюс: плавный, мало памяти, без всплесков
- Минус: приближённый (но очень точный на практике)
Token Bucket
Ведро наполняется токенами с постоянной скоростью. Каждый запрос потребляет токен. Пустое ведро = отказ. Позволяет контролируемые всплески.
- Плюс: допускает всплески, плавный
- Минус: чуть сложнее реализация
Leaky Bucket
Запросы попадают в очередь и обрабатываются с фиксированной скоростью. Полная очередь = отброс.
- Плюс: максимально ровная скорость
- Минус: добавляет задержку, нет всплесков
Реализация на Redis
-- Sliding window counter (Lua-скрипт)
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
local count = redis.call('ZCARD', key)
if count < limit then
redis.call('ZADD', key, now, now .. math.random())
redis.call('EXPIRE', key, window)
return 0 -- разрешено
else
return 1 -- ограничено
end
HTTP-заголовки ответа
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1679529600
Retry-After: 30
При ограничении — 429 Too Many Requests:
HTTP/1.1 429 Too Many Requests
Retry-After: 30
{"error": "Превышен лимит запросов. Повторите через 30 секунд."}
По чему ограничивать
- IP: просто, но общие IP (NAT) затрагивают нескольких пользователей
- API-ключ: точнее для аутентифицированных API
- Аккаунт: per-user лимиты независимо от IP
- Эндпоинт: разные лимиты (логин: 5/мин, поиск: 30/мин)
- Комбинация: IP + endpoint для анонимных, user + endpoint для авторизованных
Лучшие практики
- Информативные заголовки: лимит, остаток, время сброса
- Код 429: не 403 и не 503
- Дифференциация по тарифу: Free 50/день, Pro 5000/день
- Агрессивный лимит на логин: 5-10 попыток/мин против брутфорса
- Graceful degradation: кешированные ответы вместо жёсткого отказа
- Логирование: мониторьте кто и почему попадает под лимиты
Заключение
Rate limiting — необходимая инфраструктура для любого API. Начните со sliding window counter, реализуйте per-key лимиты и всегда сообщайте лимиты через заголовки. Хорошая реализация защищает сервис, сохраняя хороший developer experience.
Проверьте ваш сайт прямо сейчас
Проверить →