Реализация API Rate Limiting
Rate Limiting для API в распределённых системах требует консистентного управления состоянием, правильного выбора алгоритма и чёткой коммуникации с клиентами.
Краткое описание
API Rate Limiting описывает техническую реализацию ограничений на количество запросов к API. В распределённых системах лимит должен соблюдаться согласованно на всех инстансах, что требует централизованного хранилища вроде Redis или базы данных. Основные алгоритмы: Fixed Window, Sliding Window, Token Bucket и Leaky Bucket. Каждый имеет разные характеристики по справедливости, поведению при всплесках нагрузки и сложности реализации. При разработке нужно учитывать идентификацию клиента, атомарные операции, заголовки статуса, обработку ошибок с кодом 429 и Retry-After, выбор оптимальных лимитов. Rate Limiting стоит конфигурировать не только в коде приложения, но и на уровне API Gateway или Load Balancer для защиты в несколько слоёв.
Ключевые компоненты
Идентификация клиента
Rate Limiting требует уникальной идентификации клиента. Для этого используют API ключи, ID пользователя, IP-адреса или комбинации нескольких факторов. Для аутентифицированных клиентов ID пользователя обычно наилучший выбор, так как IP-адреса меняются и несколько клиентов могут находиться за одним адресом.
Реализация Fixed Window
Fixed Window считает запросы в фиксированных временных окнах. В Redis можно хранить счётчик для каждого окна и клиента. Счётчик создаётся при первом запросе в окне и истекает по прошествии времени. Если счётчик превышает лимит, запрос отклоняется.
Реализация Sliding Window
Sliding Window справедливее Fixed Window, но сложнее в реализации. Один из вариантов: Sliding Window Log, при котором для каждого клиента хранится список временных меток последних запросов. Запросы вне окна удаляются. Если количество оставшихся запросов больше лимита, новый запрос отклоняется.
Реализация Token Bucket
Token Bucket часто используемый алгоритм. Корзина имеет максимальную ёмкость и заполняется токенами с постоянной скоростью. Каждый запрос потребляет один токен. Если корзина пуста, запрос отклоняется или задерживается. Redis хорошо подходит для Token Bucket с использованием Lua-скриптов для атомарных операций.
Реализация Leaky Bucket
Leaky Bucket обрабатывает запросы с постоянной скоростью. Запросы буферизуются в очереди и обрабатываются равномерно. Алгоритм хорош для плавной обработки, но может привести к задержкам и выбросу запросов при переполнении очереди.
Redis для распределённого Rate Limiting
Redis предлагает быстрые атомарные операции и TTL, что делает его идеальным для Rate Limiting. Lua-скрипты позволяют выполнять несколько команд атомарно. Redis Cluster или Redis Sentinel обеспечивают высокую доступность. При очень высоких требованиях Redis может стать узким местом, поэтому дополнительно можно использовать кэширование или локальные proxy-кэши.
Атомарные операции
Rate Limiting должен быть атомарным, чтобы избежать race conditions. Если несколько запросов одновременно проверяют наличие оставшегося лимита, нельзя допустить его превышение. Lua-скрипты в Redis или транзакции в базах данных гарантируют атомарность.
Заголовки для отображения статуса
Клиентов необходимо информировать о их текущем статусе. Заголовки X-RateLimit-Limit, X-RateLimit-Remaining и X-RateLimit-Reset широко распространены. Альтернативно можно использовать RFC-RateLimit схему. Эти заголовки позволяют клиентам корректировать частоту своих запросов.
Обработка ошибок с кодом 429
Когда лимит превышен, API отвечает 429 Too Many Requests. Заголовок Retry-After должен указать, когда клиент может повторить попытку. Ответ об ошибке должен следовать формату RFC 7807 Problem Details и не содержать внутренних деталей.
Лимиты по эндпоинтам и по пользователям
Разные эндпоинты имеют разные затраты. Операции записи и дорогие запросы должны иметь более строгие лимиты, чем простые операции чтения. Аналогично, разные группы пользователей могут получить разные лимиты, например бесплатные и платные клиенты.
Rate Limiting на уровне API Gateway
API Gateway вроде Kong, Nginx или AWS API Gateway предлагают встроенное Rate Limiting. Они защищают API до того, как запросы достигнут бэкенда. Gateway особенно эффективны для глобальных лимитов и простых правил, в то время как код приложения может реализовать более сложные, специфичные для пользователя правила.
Практический пример
Node.js сервис реализует Token Bucket Rate Limiting с использованием Redis.
const redis = require('redis');
const client = redis.createClient();
const LIMIT = 100;
const WINDOW_SECONDS = 60;
async function isAllowed(clientId) {
const key = `rate_limit:${clientId}`;
const lua = `
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = redis.call('GET', key)
if current == false then
current = 0
end
current = tonumber(current)
if current >= limit then
return 0
end
redis.call('INCR', key)
if current == 0 then
redis.call('EXPIRE', key, window)
end
return 1
`;
const result = await client.eval(lua, { keys: [key], arguments: [String(LIMIT), String(WINDOW_SECONDS)] });
return result === 1;
}
Middleware в Express:
async function rateLimit(req, res, next) {
const clientId = req.user?.id || req.ip;
const allowed = await isAllowed(clientId);
if (!allowed) {
res.set('Retry-After', String(WINDOW_SECONDS));
return res.status(429).json({
type: 'https://api.example.com/problems/rate-limit-exceeded',
title: 'Rate Limit превышен',
status: 429,
detail: `Вы превысили лимит в ${LIMIT} запросов на ${WINDOW_SECONDS} секунд.`
});
}
next();
}
Такой подход атомарен, работает на нескольких серверных инстансах и ясно сообщает клиентам о лимитах.
FAQ: Реализация API Rate Limiting
1. Как идентифицировать клиента для Rate Limiting?
2. Почему Redis подходит для Rate Limiting?
3. Что такое Token Bucket в реализации?
4. Что такое атомарное Rate Limiting?
5. Что такое Sliding Window Log?
6. Что такое Fixed Window?
7. Что такое Leaky Bucket?
8. Что такое race condition при Rate Limiting?
9. Какие заголовки нужно устанавливать при Rate Limiting?
10. Что такое 429 Too Many Requests?
11. Должен ли Rate Limiting находиться на Gateway или в коде?
12. Что такое Per-Endpoint лимиты?
13. Что такое узкое место Rate Limiting?
14. Какая хорошая стратегия Rate Limiting?
15. Какая польза от Retry-After?
Продолжение обучения по API
Следующая статья в цикле про API посвящена Rate Limiting и Throttling для APIs — стратегиям и алгоритмам rate limiting, а также различиям между rate limiting и throttling.
Источники
- https://www.rfc-editor.org/rfc/rfc6585
- https://redis.io/docs/manual/programmability/eval-intro/
- https://www.konghq.com/kong
Книги по разработке API
Если хочешь углубить знания о rate limiting, распределённых системах и архитектуре API, рекомендуем следующие книги:
Keine Bücher für Kategorie "api-development" gefunden.



