Rate limiting: защита от перегрузки

Как ограничить число запросов от клиента и защитить систему от злоупотреблений и пиков.

Rate limiting — ограничение числа запросов от клиента за интервал времени, чтобы защитить систему от перегрузки, abuse и каскадных отказов.

Зачем

Без лимитов один агрессивный клиент (бот, цикл с багом, атака) способен исчерпать ресурсы и уронить сервис для всех. Rate limiting вводит честные квоты: например, 100 запросов в минуту на пользователя, лишние получают ответ 429 Too Many Requests.

Основные алгоритмы

АлгоритмИдея
Token bucketБакет токенов пополняется со временем; запрос тратит токен
Leaky bucketЗапросы вытекают с постоянной скоростью, очередь сглаживает
Fixed windowN запросов на фикс. окно; просто, но всплеск на границе
Sliding windowСкользящее окно; точнее, чуть дороже

Token bucket — частый дефолт: допускает короткие всплески (накопленные токены), но держит среднюю скорость.

Где ставить

Лимитер обычно живёт на границе — в API Gateway или перед приложением. Решение «пропустить/отклонить» должно быть очень быстрым, чтобы не стать само узким местом.

Клиент -> [Rate limiter] --разрешено--> App
               |--превышено--> 429 Too Many Requests

Как работает под капотом

В горизонтально масштабированной системе лимитер должен быть распределённым: счётчики хранят в общем Redis, иначе на каждом инстансе будет свой счётчик и реальный лимит умножится на число инстансов. Атомарность инкремента (INCR + EXPIRE) гарантирует корректный учёт. В ответе полезно отдавать заголовки X-RateLimit-Remaining и Retry-After, чтобы клиент знал квоту.

Частые ошибки

  • Хранить счётчики локально на инстансах -> лимит «размножается».
  • Сделать лимитер тяжёлым и превратить его в узкое место.
  • Не отдавать клиенту заголовки с остатком квоты и Retry-After.

Итог

  • Rate limiting защищает от перегрузки и злоупотреблений, отдавая 429.
  • Token bucket — удобный дефолт: всплески + средняя скорость.
  • В масштабе лимитер распределённый (Redis), счётчики общие и атомарные.
Проверьте себя
1. Какой код ответа обычно возвращают при превышении лимита запросов?
A403 Forbidden
B429 Too Many Requests
C500 Internal Server Error
D404 Not Found
2. Почему в горизонтально масштабированной системе лимитер делают распределённым (на Redis)?
ARedis быстрее любого алгоритма
BИначе у каждого инстанса свой счётчик и реальный лимит умножается на число инстансов
CЛокальные счётчики невозможно реализовать
DЭто требование HTTP
3. Чем удобен алгоритм token bucket?
AПолностью запрещает любые всплески
BДопускает короткие всплески за счёт накопленных токенов, держа среднюю скорость
CНе требует хранить состояние
DРаботает только на одном сервере