Алгоритмы токен-бакета и скользящего окна, квоты, ограничение скорости и заголовок Retry-After
Ограничение скорости запросов — это механизм, который ограничивает количество запросов, которые клиент может отправить на сервер за определенный промежуток времени. Это критически важная мера для защиты сервиса от перегрузки, DDoS-атак и злоупотреблений.
| Алгоритм | Производительность | Справедливость | Burst поддержка | Сложность реализации | Пример использования |
|---|---|---|---|---|---|
| Token Bucket | Высокая | Средняя | Да (взрывной режим) | Низкая | API с высокой нагрузкой, где допустимы всплески |
| Sliding Window | Средняя | Высокая | Нет | Средняя | Сервисы, где важно равномерное распределение квоты |
| Fixed Window | Высокая | Низкая | Нет | Низкая | Простые случаи, MVP |
| Leaky Bucket | Высокая | Высокая | Нет | Средняя | Системы с постоянным потоком запросов |
Token Bucket (Токен-бакет):
429 Too Many Requests.Sliding Window (Скользящее окно):
HTTP-запрос с превышением лимита:
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
Retry-After: 60
Content-Type: application/problem+json
{
"type": "https://api.example.com/errors/rate-limit",
"title": "Too Many Requests",
"status": 429,
"detail": "You have exceeded your request limit. Please try again in 60 seconds.",
"instance": "/api/v1/users"
}Реализация на сервере (Node.js/Redis):
const redis = require('redis');
const client = redis.createClient();
// Middleware для rate limiting
const rateLimit = (limit = 100, windowMs = 60 * 1000) => {
return async (req, res, next) => {
const key = `rate-limit:${req.ip}`;
// Получаем текущее время и количество запросов
const now = Date.now();
const windowStart = now - windowMs;
// Удаляем старые записи (вне окна)
await client.zremrangebyscore(key, 0, windowStart);
// Получаем количество запросов в текущем окне
const count = await client.zcount(key, windowStart, now);
if (count >= limit) {
// Превышен лимит
const retryAfter = Math.ceil((windowStart + windowMs - now) / 1000);
return res.status(429).json({
type: 'https://api.example.com/errors/rate-limit',
title: 'Too Many Requests',
status: 429,
detail: `You have exceeded your request limit. Please try again in ${retryAfter} seconds.`,
instance: req.path
});
}
// Добавляем текущий запрос в список
await client.zadd(key, now, now.toString());
await client.expire(key, Math.ceil(windowMs / 1000));
// Устанавливаем заголовки
res.setHeader('X-RateLimit-Limit', limit);
res.setHeader('X-RateLimit-Remaining', limit - count - 1);
res.setHeader('Retry-After', Math.ceil((windowStart + windowMs - now) / 1000));
next();
};
};
// Использование
app.use('/api/*', rateLimit(100, 60 * 1000)); // 100 запросов в минуту| Этап | Действия | Ответственные | Сроки |
|---|---|---|---|
| Проектирование | Определение лимитов, выбор алгоритма, планирование стратегии депрекации | Архитектор, Security Engineer | На этапе проектирования API |
| Разработка | Реализация middleware, интеграция с Redis, тестирование граничных случаев | Разработчики | В процессе разработки |
| Тестирование | Нагрузочное тестирование, проверка поведения при превышении лимитов | QA, DevOps | Перед PR и в CI |
| Релиз | Документация для клиентов, анонс лимитов, мониторинг | Technical Writer, Release Manager | При релизе |
| Поддержка | Анализ использования, корректировка лимитов, обработка жалоб | DevOps, Support Team | Постоянно |
Q: Как работает ограничение скорости запросов с алгоритмом токен-бакета?
A: Токен-бакет: пополнение, забор, 429, взрывной режим. Токен-бакет: capacity=100, refill_rate=10/sec. Запрос: взять 1 токен. Если пустой → 429 Too Many Requests, Retry-After: 1. Взрывной режим: до 100 запросов сразу, если бакет полон.
Q: Какой HTTP-статус код возвращается при превышении лимита запросов?
A: Код 429 Too Many Requests используется для указания на то, что пользователь отправил слишком много запросов за определенный период времени. Сервер должен включать заголовок Retry-After, чтобы клиент знал, когда можно повторить запрос.
Q: В чем преимущество скользящего окна (sliding window) перед фиксированным? A: Скользящее окно предотвращает ситуацию, когда в начале нового периода (например, новой минуты) клиент может сделать в два раза больше запросов, чем лимит, потому что предыдущий период еще не закончился. Оно обеспечивает более равномерное использование квоты.
Q: Какие заголовки обычно используются для информирования клиента о лимитах?
A: Стандартные заголовки для rate limiting: X-RateLimit-Limit (общий лимит), X-RateLimit-Remaining (оставшееся количество запросов) и Retry-After (время в секундах до следующей попытки).
Rate Limiting — это критически важная мера для защиты сервиса от перегрузки, DDoS-атак и злоупотреблений.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.