GH GambleHub

Проектирование rate limiter’ов

1) Зачем rate limiting

Rate limiting защищает доступность и экономику API: останавливает флуды, «бурсты» ретраев, credential stuffing, защищает дорогие операции (денежные транзакции, генерацию отчетов), сглаживает нагрузку на зависимые системы (БД/провайдеры). Хороший дизайн дает справедливость (fairness), предсказуемость латентности и четкие SLO.

Ключевые цели

Стабильность RPS и защита бэкенда от перегрузки.
Управляемая «эластичность» (burst allowance).
Дифференциация клиентов (пер-пользователь/пер-организация/пер-ключ/пер-IP/пер-регион).
Стоимостная модель: разные «цены» для разных операций.

2) Типы лимитов

RPS-лимиты: запросов в секунду/минуту.
Квоты: суммарный бюджет в период (день/месяц).
Конкурентность: одновременные операции (checkout, heavy job).
Скорость/полоса: байт/сек (загрузка/выгрузка).
Взвешенные лимиты: «стоимость» запроса по сложности (например, GraphQL complexity, размер batch).
Адаптивные: ужесточаются при аномалиях (подозрительная активность/ошибки 401/403/5xx).

3) Алгоритмы и когда их применять

3.1 Fixed window counter

Просто: счетчик на интервал (например, 100 r/min).
Плюсы: минимальная стоимость. Минусы: «краевые берсты» на границах окна.

Когда: панели админок, невысокая точность, низкая стоимость.

3.2 Sliding window (log / counter)

Log: хранит метки времени последних запросов, точен, дорог по памяти.
Counter: среднее двух соседних окон (rolling), компромисс точности и цены.

Когда: публичные API среднего трафика, нужна плавность без сложной математики.

3.3 Token bucket

Параметры: скорость `r` (токенов/сек) и емкость `b` (burst). Каждый запрос «сжигает» токен.
Плюсы: естественный burst allowance, простая реализация. Минусы: нет строгой равномерности.

Когда: почти всегда для RPS, если нужны «залпы» в пределах `b`.

3.4 Leaky bucket (drip)

Очередь, из которой «утекает» с фиксированной скоростью.
Плюсы: ровный выходной поток. Минусы: больше задержек.

Когда: сглаживание к внешним «хрупким» провайдерам.

3.5 GCRA (Generalized Cell Rate Algorithm)

Модель теоретического времени прибытия (TAT):
  • `TAT_next = max(TAT_current, now) + 1/r`, запрос принят, если `now <= TAT_current + burst/r`.
  • Плюсы: строгая, точная, мало памяти (по ключу храним TAT). Минусы: сложнее для понимания.

Когда: нужен строгий контроль и плавность, распределенные лимиты.

3.6 Конкурентные семафоры

Счетчик активных операций; вход — если «билеты» есть; выход — освобождение.
Когда: long-running операции, потоки, WebSocket, загрузки.

4) Модель ключей лимитов

Ключ = комбинация атрибутов:
  • `client_id`/`api_key`/`user_id`/`org_id`
  • `IP/ASN/гео` (грубая защита)
  • `endpoint/method` (горячие маршруты)
  • `scope/plan/tier` (монетизация)
  • `idempotency_key` (write-операции)
  • Используйте иерархию: сначала строгие пер-ключ, затем пер-организация, затем глобальные.

5) Вес запроса (cost model)

Определяйте «стоимость» `cost(q)`:
  • GraphQL: сложность по полям × глубина.
  • REST: размер ответа/запроса, тип операции (read=1, write=3, отчет=10).
  • Batch: `cost = min(n, cap)`.
  • Лимитируем токены, а не «запросы»: `budget -= cost(q)`.

6) Распределенная реализация

6.1 Хранилища

In-process: ультра-быстро, но не общий лимит (годится для локальных «мягких» лимитов).
Redis: де-факто стандарт. INCR/EXPIRE, Lua-скрипты (атомарность), ZSET для sliding window, ключи с TTL.
Envoy/NGINX/Kong/Traefik: встроенные фильтры; удобно для периметра.
Service Mesh: локальные лимиты на sidecar + глобальная синхронизация.

6.2 Атомарность и гонки

Lua в Redis: проверка и инкремент в одном шаге.
GCRA: хранить один TAT с CAS/скриптом.
Согласованность часов: NTP, монотонные таймеры.
Sharding: консистентный хеш по ключу; избегайте «горячих» шардов.

6.3 Геораспределение

Локальные лимиты на региональных кластерах + верхний глобальный (coarse).
CRDT/репликация — осторожно (задержки, двойной расход). Предпочтительнее региональные лимиты с запасом.

7) Политики и приоритизация

Планы: Free/Pro/Enterprise с разными `r`, `b`, квотами.
Приоритеты: «дорогие» маршруты получают меньший лимит или больший cost.
Списки: allow-list для интеграций, deny по ASN/прокси/ТОR.
Эскалация: при повторном превышении — понижаем лимит, вводим proof-of-work/капчу/челленджи.

8) Примеры конфигов

8.1 Envoy (HTTP rate limit filter, псевдо)

yaml rate_limit:
domain: public-api descriptors:
- key: api_key rate_limit:
unit: second requests_per_unit: 50 burst: 100
- key: api_key value: payments. write rate_limit:
unit: second requests_per_unit: 5 burst: 10

8.2 NGINX (lua + Redis, псевдо)

nginx lua_shared_dict limits 10m;

location /api/ {
access_by_lua_block {
local key = ngx. var. arg_apikey.. ":".. ngx. var. request_method.. ":".. ngx. var. uri
-- token bucket in Redis (evalsha)
local allowed, retry_after = ratelimit_allow(key, 50, 100) -- r=50/s, b=100 if not allowed then ngx. header["Retry-After"] = retry_after return ngx. exit(429)
end
}
proxy_pass http://backend;
}

8.3 Конкурентные лимиты (псевдокод)

pseudo on_request_start(key):
if redis. incr_with_ttl("sem:" + key, ttl=60) > MAX_CONCURRENCY:
redis. decr("sem:" + key); reject(429)
on_request_finish(key):
redis. decr("sem:" + key)

8.4 GCRA (псевдокод)

pseudo params: r tokens/sec, burst b tat = redis. get(key) or now allowed_time = tat - (b / r)
if now < allowed_time: reject(429, retry_after = allowed_time - now)
tat_next = max(tat, now) + 1/r redis. set(key, tat_next, ttl = ceil(b/r) + safety)

9) Интеграция с ретраями, таймаутами и circuit breaker

Retry-budget: ограничьте долю ретраев до X% от основного трафика.
Jitter: при backoff всегда добавляйте джиттер — уменьшает синхронные всплески.
Circuit breaker: при высокой ошибочности (`5xx`, timeouts) снижайте лимиты или переводите часть маршрутов в «read-only».
Hedging: аккуратно; учитывайте cost, чтобы не удваивать расход бюджета.

10) Наблюдаемость и управление

Метрики: `rps_allowed`, `rps_blocked`, `429_rate`, `retry_after_avg`, `burst_used`, `quota_remaining`, `active_concurrency`.
Лейблы: по ключу лимита, региону, эндпоинту, плану.
Логи решений (сэмплированные): причина отказа, текущие счетчики, TTL ключа.
Дашборды: тепловые карты по ключам/эндпоинтам, «горячие» клиенты.
Алерты: рост 429>2–5% на критичных маршрутах, частые «исчерпания» квот, дисбаланс шардов.

11) Тестирование и валидация

Контрактные тесты политик (таблицы «если-то»).
Нагрузочные: бурсты (x10 от r), длительные плато, «грязные» паттерны (slow-POST, долгие соединения).
Chaos-трафик: неравномерные потоки, clock drift, выпадение Redis/mesh.
А/В-включение: canary rollout лимитов, shadow-решения (логируем, но не блокируем) перед включением.

12) Edge-кейсы и тонкости

Clock skew: используйте `now()` из единого источника (сервер), а не из заголовков клиента.
Idempotency-Key: для write — снижает amplification при ретраях.
Batch-операции: лимитируйте по размеру батча и по суммарному cost.
Long-poll/WebSocket: лимитируйте по количеству каналов/подписок и по длительности.
Cold start: «теплый» старт счетчиков/предзагрузка; иначе всплески ложных 429.
Вычислительно дорогие запросы: лимитируйте до выполнения бизнес-логики.
Границы TTL: TTL ключей должен покрывать окно + запас (safety margin).

13) Антибот-эскалации

Ступени: предупреждение → 429 + `Retry-After` → чаллендж (капча/пазл) → временный блок.
Сигналы: device-fingerprint, поведение курсора/тайминги, TOR/прокси/хостинги.
Политики должны быть детерминированны и воспроизводимы для форензики.

14) Безопасность и комплаенс

Deny-by-default на критичных маршрутах (write/финансы).
Аудит: храните решения по лимитам для регуляторных кейсов и разборов инцидентов.
PII: ключи лимитов не должны раскрывать персональные данные в логах.

15) Чек-лист prod-готовности

  • Определены ключи лимитов и cost-модель.
  • Выбран алгоритм (token bucket/GCRA) и хранилище (Redis/шлюз).
  • Политики для tier’ов клиентов + глобальные «предохранители».
  • Конкурентные лимиты для долгих операций.
  • Retry-budget, backoff с джиттером, интеграция с circuit breaker.
  • Дашборды/алерты, сэмплированные логи решений.
  • Canary-включение и shadow-режим.
  • Тесты бурстов, длительных плато, сбоев Redis, clock skew.
  • Документация для клиентов: коды 429, `Retry-After`, примеры экспоненциального backoff.

16) TL;DR

Используйте token bucket или GCRA с Redis/шлюзом, проектируйте ключи лимитов и стоимость запросов, добавляйте конкурентные семафоры для долгих операций, интегрируйте с retry-budget и circuit breaker, наблюдайте за 429 и «бурст-емкостью», раскатывайте лимиты через canary/shadow и обязательно тестируйте бурсты и отказ хранилища.

Contact

Свяжитесь с нами

Обращайтесь по любым вопросам или за поддержкой.Мы всегда готовы помочь!

Telegram
@Gamble_GC
Начать интеграцию

Email — обязателен. Telegram или WhatsApp — по желанию.

Ваше имя необязательно
Email необязательно
Тема необязательно
Сообщение необязательно
Telegram необязательно
@
Если укажете Telegram — мы ответим и там, в дополнение к Email.
WhatsApp необязательно
Формат: +код страны и номер (например, +380XXXXXXXXX).

Нажимая кнопку, вы соглашаетесь на обработку данных.