PRO

Как реализовать rate limiter?

Rate limiter ограничивает частоту запросов по ключу, например IP-адресу, user ID или API key, и защищает сервис от злоупотребления и перегрузки. Для распределённой системы состояние лимитов хранят в общем быстром хранилище, обычно Redis, а проверку и обновление счётчика выполняют атомарно.
Подробный ответ

Что ограничивать

Лимиты можно задавать на IP, пользователя, организацию, API key, endpoint или их комбинацию. Например, login endpoint обычно имеет более строгий лимит, чем чтение публичных данных, а premium-клиенты могут иметь отдельный тариф.

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

АлгоритмПлюсыМинусы
Fixed windowПростой и дешёвыйДопускает burst на границе временных окон
Sliding logТочныйТребует хранить timestamps запросов
Sliding window counterБаланс точности и стоимостиСложнее fixed window
Token bucketРазрешает контролируемые bursts и ограничивает среднюю скоростьНужно корректно хранить состояние
Leaky bucketСглаживает выходной потокМожет увеличивать ожидание запросов

Token bucket

У клиента есть bucket с максимальным числом токенов. Токены добавляются с фиксированной скоростью. Каждый запрос расходует один токен; если токенов нет, запрос отклоняется с 429 Too Many Requests. Такой алгоритм допускает кратковременный burst до размера bucket.

Распределённая архитектура

Client
  |
CDN / API Gateway
  |
Rate limiter middleware
  |
Redis Cluster
  |
Application services

В памяти одного API-instance хранить лимит нельзя: запросы одного клиента могут попасть на разные реплики. Redis даёт общее состояние, а Redis Lua script или атомарная команда позволяют выполнить проверку и обновление без race condition.

Пример Redis-ключа

ratelimit:{tenant_id}:{user_id}:{endpoint}

Поведение при превышении лимита

Сервис возвращает 429 Too Many Requests. При необходимости добавляют Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining и другие согласованные заголовки, чтобы клиент мог корректно замедлиться.

Отказ Redis

Нужно заранее выбрать политику fail-open или fail-closed. Fail-open сохраняет доступность API, но временно ослабляет защиту. Fail-closed лучше защищает дорогие и чувствительные endpoints, но может заблокировать легитимных пользователей при сбое лимитера. Решение зависит от риска конкретного endpoint.

Как ответить на собеседовании

Я выбираю ключ лимита, например user ID или API key, и алгоритм — часто token bucket либо sliding window. В распределённой системе храню состояние в Redis и использую атомарное обновление, чтобы несколько реплик не создали race condition. При превышении возвращаю 429, собираю метрики срабатываний и отдельно определяю fail-open/fail-closed политику для разных API.

Оцени свой прогресс

Честно оцени своё понимание этого вопроса, чтобы мы могли построить твой учебный трек максимально эффективно.
Читать в блоге