Обмеження частоти запитів (rate limiting), також відоме як регулювання запитів (request throttling), — це механізм контролю швидкості, з якою користувач або сервіс може надсилати запити до API або сервера, що запобігає зловживанням, забезпечує справедливий розподіл ресурсів та підтримує стабільність системи.
Розуміння обмеження частоти запитів
Обмеження частоти запитів захищає сервіси від надмірних обсягів запитів, які можуть призвести до погіршення продуктивності, атак типу "відмова в обслуговуванні" (DoS) або вичерпання ресурсів. Воно гарантує, що системні ресурси доступні всім легітимним користувачам і запобігає монополізації доступу однією сутністю. Проксі-сервіс часто відіграє вирішальну роль у застосуванні цих обмежень, або застосовуючи їх до клієнтських запитів до того, як вони досягнуть вищестоящих сервісів, або повідомляючи клієнтам про вищестоящі обмеження.
Навіщо впроваджувати обмеження частоти запитів?
- Захист ресурсів: Запобігає перевантаженню серверів занадто великою кількістю запитів, зберігаючи CPU, пам'ять та пропускну здатність мережі.
- Запобігання зловживанням: Зменшує кількість атак методом перебору, підстановки облікових даних та інших шкідливих дій шляхом обмеження спроб запитів.
- Справедливе використання: Забезпечує рівний доступ усіх клієнтів до спільних ресурсів, запобігаючи монополізації системи одним клієнтом.
- Контроль витрат: Для сервісів із тарифікацією за використання обмеження частоти запитів можуть допомогти контролювати операційні витрати, обмежуючи споживання ресурсів.
Поширені алгоритми обмеження частоти запитів
Для відстеження та застосування обмежень частоти запитів використовуються різні алгоритми, кожен з яких має відмінні характеристики щодо обробки сплесків та використання ресурсів.
Маркерний кошик (Token Bucket)
Алгоритм "Маркерний кошик" моделює кошик з фіксованою ємністю, який поповнюється маркерами з постійною швидкістю. Кожен запит споживає один маркер. Якщо кошик порожній, запит відхиляється або ставиться в чергу. Цей алгоритм дозволяє деяку "сплесковість", оскільки запити можуть споживати кілька маркерів, якщо вони доступні, до ємності кошика.
Дірявий кошик (Leaky Bucket)
Алгоритм "Дірявий кошик" обробляє запити з фіксованою вихідною швидкістю. Запити додаються до черги ("кошика"). Якщо черга заповнена, нові запити відхиляються. Запити "витікають" з кошика з постійною швидкістю, забезпечуючи стабільний потік обробки. Цей алгоритм згладжує сплески, але вносить затримку для запитів, які повинні чекати в черзі.
Лічильник з фіксованим вікном (Fixed Window Counter)
В алгоритмі "Лічильник з фіксованим вікном" визначається часове вікно (наприклад, 60 секунд), і лічильник відстежує запити в межах цього вікна. Після закінчення вікна лічильник скидається. Запити, що перевищують ліміт у межах вікна, відхиляються. Недоліком є "проблема сплеску" на краях вікна, коли клієнти можуть надсилати подвійну дозволену кількість запитів протягом двох послідовних вікон.
Журнал ковзного вікна (Sliding Window Log)
Алгоритм "Журнал ковзного вікна" записує мітку часу для кожного запиту. Коли надходить новий запит, система підраховує кількість міток часу за останні N секунд (вікно). Якщо ця кількість перевищує ліміт, запит відхиляється. Цей метод є точним, але може бути ресурсоємним через зберігання всіх міток часу.
Лічильник ковзного вікна (Sliding Window Counter)
Цей алгоритм поєднує аспекти "Фіксованого вікна" та "Журналу ковзного вікна", щоб пом'якшити проблему країв без накладних витрат на пам'ять від логування кожного запиту. Він використовує два фіксованих вікна: поточне вікно та попереднє вікно. Мітка часу поточного запиту визначає його позицію в поточному вікні. Дозволені запити розраховуються як зважене середнє значення кількості запитів попереднього вікна та кількості запитів поточного вікна, на основі частки поточного вікна, що минула.
Порівняння алгоритмів
| Особливість | Маркерний кошик | Дірявий кошик | Лічильник з фіксованим вікном | Журнал ковзного вікна | Лічильник ковзного вікна |
|---|---|---|---|---|---|
| Обробка сплесків | Дозволяє сплески | Згладжує сплески | Схильний до сплесків | Добре обробляє сплески | Добре обробляє сплески |
| Використання ресурсів | Помірне | Помірне | Низьке | Високе (пам'ять) | Низьке до помірного |
| Складність | Помірна | Помірна | Низька | Висока | Помірна |
| Точність | Добра | Добра | Погана (крайові випадки) | Висока | Добра |
| Вплив на затримку | Низький (якщо є маркери) | Високий (черга) | Низький | Низький | Низький |
Визначення обмеження частоти запитів
Коли ліміт частоти запитів перевищено, API або сервіс зазвичай відповідає певними кодами стану HTTP та заголовками.
HTTP-код стану 429 Too Many Requests
Стандартний HTTP-код стану для обмеження частоти запитів — 429 Too Many Requests. Це вказує на те, що користувач надіслав занадто багато запитів за певний проміжок часу.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json
{
"error": "Rate limit exceeded. Try again in 30 seconds."
}
Заголовки відповіді
API часто включають специфічні заголовки, щоб надати більше контексту про статус обмеження частоти запитів та як його обробляти.
Retry-After: (RFC 7231, Розділ 7.1.3) Вказує, як довго агент користувача повинен чекати перед тим, як зробити наступний запит. Його значення може бути цілим числом, що представляє секунди, або конкретною датою/часом.X-RateLimit-Limit: Максимальна кількість запитів, дозволена в поточному вікні обмеження частоти запитів.X-RateLimit-Remaining: Кількість запитів, що залишилися в поточному вікні обмеження частоти запитів.X-RateLimit-Reset: Час (зазвичай секунди Unix epoch), коли поточне вікно обмеження частоти запитів скидається.
Ці заголовки X-RateLimit-* є поширеними, але не стандартизовані RFC; їх точна назва та поведінка можуть відрізнятися між сервісами.
Обробка обмежень частоти запитів
Ефективна клієнтська обробка обмежень частоти запитів є вирішальною для створення надійних застосунків, які взаємодіють із зовнішніми сервісами.
Експоненційна затримка з коливанням (Exponential Backoff with Jitter)
Це стандартна стратегія для повторних спроб невдалих запитів, включаючи ті, що виникли через обмеження частоти запитів.
- Експоненційна затримка: Клієнт чекає експоненційно зростаючий проміжок часу між повторними спробами (наприклад, 1 секунда, потім 2 секунди, потім 4 секунди, потім 8 секунд).
- Коливання (Jitter): До періоду затримки додається невелика випадкова затримка. Це запобігає одночасному повторному надсиланню запитів усіма клієнтами після скидання обмеження частоти запитів, що може спровокувати нову хвилю обмежень.
import time
import random
import requests
def make_request_with_retry(url, max_retries=5):
retries = 0
while retries < max_retries:
try:
response = requests.get(url)
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 1))
print(f"Rate limited. Retrying after {retry_after} seconds.")
time.sleep(retry_after)
elif 200 <= response.status_code < 300:
return response
else:
response.raise_for_status() # Raise an exception for other HTTP errors
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
# Exponential backoff with jitter
delay = (2 ** retries) + random.uniform(0, 1) # 2^retries + random float between 0 and 1
print(f"Retrying in {delay:.2f} seconds...")
time.sleep(delay)
retries += 1
raise Exception(f"Failed to make request after {max_retries} retries.")
# Example usage:
# response = make_request_with_retry("https://api.example.com/data")
# if response:
# print("Request successful:", response.json())
Дотримання заголовка Retry-After
Якщо API надає заголовок Retry-After, клієнти повинні дотримуватися цієї директиви. Значення вказує мінімальний час очікування перед надсиланням іншого запиту до тієї ж кінцевої точки.
Клієнтське кешування
Кешуйте відповіді від часто доступних, незмінних кінцевих точок. Це зменшує кількість запитів, що надсилаються до API, опосередковано допомагаючи залишатися в межах обмежень частоти запитів.
Пакетна обробка запитів
Якщо API підтримує це, об'єднуйте кілька менших операцій в один, більший запит. Це зменшує загальну кількість викликів API.
Проактивне регулювання (Predictive Throttling)
Клієнти можуть відстежувати власну швидкість запитів і проактивно сповільнювати або призупиняти запити, коли вони наближаються до відомих обмежень частоти запитів, замість того, щоб чекати відповіді 429. Це вимагає попереднього знання обмежень частоти запитів API.
Конфігурація проксі-сервісу для обмеження частоти запитів
Надійний проксі-сервіс пропонує комплексні функції для керування обмеженнями частоти запитів, як для клієнтів, що отримують доступ до сервісів через проксі, так і для власних взаємодій проксі з вищестоящими API.
Застосування обмежень до вхідного трафіку (Ingress Traffic)
Проксі може застосовувати обмеження частоти запитів до вхідних клієнтських запитів на основі різних критеріїв.
- IP-адреса клієнта: Обмежує запити з однієї IP-адреси.
- API-ключ/токен: Обмежує запити, пов'язані з певними обліковими даними для автентифікації.
- ID користувача: Якщо проксі може витягувати інформацію про користувача із заголовків або токенів.
- Шлях/кінцева точка: Різні обмеження частоти запитів для різних кінцевих точок API (наприклад,
/searchможе мати вищий ліміт, ніж/admin/delete).
# Приклад: Конфігурація проксі для обмеження частоти запитів за IP
http:
routers:
api-router:
rule: "Host(`api.example.com`)"
service: api-service
middlewares: [rate-limit-ip]
middlewares:
rate-limit-ip:
rateLimit:
average: 100 # запитів на секунду
burst: 50 # максимальний сплеск понад середнє
sourceCriterion: "ipStrategy" # Застосовувати ліміт для кожної вихідної IP-адреси
Керування вихідним трафіком до вищестоящих сервісів (Upstream Services)
Коли сам проксі споживає вищестоящі API, він може реалізувати власне обмеження частоти запитів, щоб запобігти перевантаженню цих зовнішніх сервісів. Це критично важливо для сценаріїв інтеграції, де проксі агрегує дані з кількох джерел.
- Обмеження, специфічні для вищестоящих сервісів: Налаштуйте окремі обмеження частоти запитів для кожного вищестоящого сервісу, з яким взаємодіє проксі.
- Розмикання ланцюга (Circuit Breaking): Поєднуйте обмеження частоти запитів з патернами розмикання ланцюга, щоб ізолювати збої, коли вищестоящий сервіс стає невідповідним або постійно обмежує частоту запитів проксі.
Налаштування та деталізація
Розширені конфігурації проксі дозволяють тонко керувати обмеженням частоти запитів:
- Динамічні обмеження: Регулюйте обмеження на основі стану бекенду, часу доби або інших операційних метрик.
- Багаторівневі обмеження: Впроваджуйте різні обмеження частоти запитів для різних рівнів клієнтів (наприклад, безкоштовні проти преміум-користувачів).
- Керування квотами: Відстежуйте використання щодо довгострокових квот (наприклад, запитів на місяць) на додаток до короткострокових обмежень частоти запитів.
Моніторинг та оповіщення
Проксі-сервіс повинен надавати інструменти для моніторингу статистики обмежень частоти запитів:
- Кількість запитів: Відстежуйте загальну кількість запитів, успішних запитів та запитів, що були обмежені.
- Перевищення лімітів: Сповіщайте, коли ліміти частоти запитів наближаються або перевищуються для конкретних клієнтів або вищестоящих сервісів.
- Тенденції використання: Візуалізуйте шаблони запитів з часом, щоб виявити потенційні вузькі місця або зловживання.
Моніторинг допомагає операційним командам розуміти шаблони трафіку, оптимізувати конфігурації обмежень частоти запитів та проактивно вирішувати проблеми до того, як вони вплинуть на доступність сервісу.