Лимиты запросов (rate limiting)
Какие виды лимитов можно настроить, как использовать плагины, как выбрать плагин для вашего случая.
Что это
Лимит запросов (rate limit) — порог запросов, которые клиент может отправить к API или которые одновременно могут находиться в обработке.
Все запросы выше порога будут заблокированы: Application Delivery Controller не пропустит их к upstream и вернёт клиенту ошибку.
Зачем нужны
Лимиты запросов помогают:
-
защитить бэкенд от перегрузки;
-
остановить клиентов, которые отправляют слишком много запросов из-за ошибки или злоупотребления;
-
распределить ресурсы между потребителями API;
-
задать разные лимиты для разных клиентов, маршрутов или тарифов;
-
сохранить предсказуемое качество сервиса при всплесках трафика.
Какой ответ получает клиент, который превысил лимит
Клиент получит одну из ошибку 503 Service Temporarily Unavailable, 414 Request-URI Too Large либо 400 Bad Request — в зависимости от того, какой лимит превысил.
В настройках плагинов limit-req, limit-count и limit-conn вы можете задать другой код ответа через rejected_code. Обычно для лимитов запросов используют 429 Too Many Requests.
Виды лимитов
Можно устанавливать лимиты по разным основаниям: Consumer, IP-адресу, Route, количеству HTTP-заголовков в запросе, размеру request line / поля HTTP-заголовка или общему трафику, который проходит через Application Delivery Controller.
request line включает в себя HTTP-метод, URI ресурса и версию протокола HTTP. Пример: GET /search?q=iphone&color=black HTTP/1.1.
|
| Вид | Как работает | Когда использовать | Что учитывать |
|---|---|---|---|
По Consumer |
Считается число запросов от конкретного Consumer. При превышении лимита запросы этого Consumer отклоняются. |
Для API с аутентификацией, тарифами, SLA или индивидуальными лимитами клиентов. |
Система должна сначала определить клиента через auth plugin: например, по API key, JWT или другому credential. |
По IP-адресу |
Считается число запросов с конкретного IP-адреса. При превышении лимита запросы с этого адреса отклоняются. |
Для публичных эндпоинтов без аутентификации: открытых справочников, health check или публичных API. |
За одним IP может находиться много реальных пользователей: например, в корпоративной сети, за NAT или у мобильного оператора. |
По Route |
Считается число запросов к конкретному Route. При превышении лимита новые запросы к этому Route отклоняются. |
Когда разные эндпоинты создают разную нагрузку на бэкенд: например, простой запрос метаданных можно лимитировать мягче, а тяжёлый поиск по большой базе — жёстче. |
Лимит нужно подбирать с учётом нагрузки, которую создаёт конкретный эндпоинт. |
Глобально |
Считается общий поток запросов через Application Delivery Controller. При превышении лимита новые запросы отклоняются независимо от клиента или маршрута. |
Как дополнительная защита всей системы, если суммарный трафик всех клиентов приближается к пределу инфраструктуры. |
Лимит лучше ставить на значение суммарной нагрузки, которое приближается к пределу инфраструктуры. |
По количеству HTTP-заголовков |
Считается количество HTTP-заголовков в запросе. Если их больше заданного |
Когда нужно защититься от запросов с чрезмерным числом заголовков: ошибок клиентов, некорректно настроенных интеграций или попыток перегрузить обработку заголовков. |
При выставлении лимита учитывайте служебные заголовки, которые добавляют прокси, WAF, CDN и клиентские SDK. |
По размеру request line и каждого отдельного поля HTTP-заголовка |
Nginx принимает request line и поля заголовков в буферы фиксированного размера. Если что-то из этого не помещается в один большой буфер, запрос отклоняется. |
Когда нужно ограничить слишком длинную request line или крупные заголовки. |
Перед установкой лимита проверьте реальные размеры возможной request line, включая query string, и отдельных заголовков, особенно |
Как установить лимиты запросов
Большинство лимитов устанавливаются с помощью плагинов:
-
limit-req— ограничивает частоту запросов в секунду; -
limit-count— ограничивает количество запросов за временной период; -
limit-conn— ограничивает количество одновременных активных запросов; -
header-count-limit— ограничивает количество HTTP-заголовков в запросе.
Лимиты по максимальному размеру request line и каждого отдельного поля HTTP-заголовка устанавливается через настройки Nginx в conf/config.yaml: client_header_buffer_size и large_client_header_buffers.
Рассказываем ниже конфигурацию и подключение плагинов, а также про лимит, который настраивается на Nginx.
limit-req
Что ограничивает. limit-req ограничивает частоту запросов: сколько запросов в секунду система будет передавать в upstream.
Как работает. Плагин работает по алгоритму leaky bucket. В этом алгоритме вы задаёте два значения:
-
rate— основную скорость обработки запросов; -
burst— небольшой люфт: число запросов, на которое клиент может кратковременно превыситьrate.
Если клиент отправляет запросы в пределах rate, система сразу передаёт их в upstream.
-
Если клиент кратковременно превышает
rate, но превышение укладывается вburst, лишние запросы не блокируются. Они становятся в очередь и постепенно отправляются в upstream с заданной скоростьюrate. -
Если очередь уже заполнена, то есть превышение стало больше
burst, новые запросы отклоняются и клиент получает ошибку, заданную вrejected_code.
Пример. Настроено:
rate = 10 burst = 20
Это значит: система стабильно выпускает в upstream 10 запросов в секунду. Если клиент отправит больше, до 20 лишних запросов могут встать в очередь. Всё, что придёт сверх этого люфта, будет отклонено.
Когда пригодится. limit-req подходит, когда нужно защитить бэкенд от резких всплесков и выровнять поток запросов.
Например, его можно использовать для эндпоинтов, которые выдерживают стабильную нагрузку, но плохо переносят резкие пики: поиск, авторизация, генерация отчётов, обращения к внешним системам.
Атрибуты.
| Атрибут | Тип | Обяз. | По умолч. | Допустимые значения | Описание |
|---|---|---|---|---|---|
|
|
Да |
|
Максимальное число запросов в секунду. Запросы сверх |
|
|
|
Да |
|
Число запросов в секунду, которые можно задержать для throttling. Запросы сверх |
|
|
|
Нет |
|
|
Тип |
|
|
Да |
|
Признак, по которому считаются запросы. Для |
|
|
|
Нет |
|
|
HTTP-код, который возвращается при отклонении запроса из-за превышения лимита. |
|
|
Нет |
|
Тело ответа, которое возвращается при отклонении запроса из-за превышения лимита. |
|
|
|
Нет |
|
Если |
|
|
|
Нет |
|
Если |
|
|
|
Нет |
|
|
Политика хранения счётчика. |
|
|
Нет |
Адрес узла Redis. Обязателен при |
||
|
|
Нет |
|
|
Порт Redis при |
|
|
Нет |
Имя пользователя Redis при использовании Redis ACL. Для legacy |
||
|
|
Нет |
Пароль Redis при |
||
|
|
Нет |
|
Если |
|
|
|
Нет |
|
Если |
|
|
|
Нет |
|
|
Номер базы данных Redis при |
|
|
Нет |
|
|
Таймаут Redis в миллисекундах при |
|
|
Нет |
|
|
Keepalive timeout для Redis в миллисекундах при |
|
|
Нет |
|
|
Размер keepalive pool для Redis при |
|
|
Нет |
Список узлов Redis Cluster. Требуется минимум два адреса. Обязателен при |
||
|
|
Нет |
Имя Redis Cluster. Обязательно при |
||
|
|
Нет |
|
Если |
|
|
|
Нет |
|
Если |
Пример конфигурации. Route с limit-req ограничивает запросы по IP-адресу клиента: 1 запрос в секунду и 1 дополнительный запрос в очереди.
{
"uri": "/get",
"limit_req": {
"rate": 1,
"burst": 1,
"key": "remote_addr",
"rejected_code": 429
}
}
limit-count
Что ограничивает. limit-count ограничивает количество запросов за заданный период. Например:
-
100 запросов в минуту;
-
10 000 запросов в день;
-
1 000 000 запросов в месяц.
Как работает. Система считает запросы, которые попадают в один счётчик лимита. Счётчик может быть привязан к Consumer, IP-адресу, Route или другому признаку.
Когда число запросов достигает заданного лимита, новые запросы отклоняются до конца текущего периода.
В распределённой установке, где работает несколько узлов Application Delivery Controller, для limit-count нужен общий счётчик. Для этого используется Redis или Redis Cluster. Тогда лимит считается не отдельно на каждом узле, а для всей группы.
|
Когда пригодится. limit-count подходит для квот: тарифных планов, SLA, дневных, минутных или месячных лимитов.
Например, его можно использовать, если клиенту разрешено не больше 10 000 запросов в день или не больше 100 запросов в минуту к конкретному Route.
Особенности. limit-count может добавлять в ответы заголовки с состоянием лимита:
| Заголовок | Что значит |
|---|---|
|
Общий лимит запросов |
|
Сколько запросов осталось до достижения лимита |
|
Через сколько секунд сбросится счётчик |
Эти заголовки помогают API-клиенту отслеживать оставшуюся квоту и заранее замедлять отправку запросов. Добавление заголовков настраивается через атрибуты.
Атрибуты.
| Атрибут | Тип | Обяз. | По умолч. | Допустимые значения | Описание |
|---|---|---|---|---|---|
|
|
Нет |
|
Максимальное число запросов за заданный интервал. Обязателен, если не настроен |
|
|
|
Нет |
|
Интервал времени в секундах, которому соответствует |
|
|
|
Нет |
Список правил rate limiting. Каждое правило содержит |
||
|
|
Да |
|
Максимальное число запросов за заданный интервал. |
|
|
|
Да |
|
Интервал времени в секундах, которому соответствует |
|
|
|
Да |
Ключ для подсчёта запросов. Если ключ не существует, правило не выполняется. Интерпретируется как комбинация переменных, например |
||
|
|
Нет |
Префикс для rate limit headers: |
||
|
|
Нет |
|
|
Тип |
|
|
Нет |
|
Признак, по которому считаются запросы. Для |
|
|
|
Нет |
|
|
HTTP-код, который возвращается при отклонении запроса из-за превышения лимита. |
|
|
Нет |
|
Тело ответа, которое возвращается при отклонении запроса из-за превышения лимита. |
|
|
|
Нет |
|
Если |
|
|
|
Нет |
|
|
Политика хранения счётчика. |
|
|
Нет |
|
Если |
|
|
|
Нет |
|
ID группы плагина. Routes с одной |
|
|
|
Нет |
Адрес узла Redis. Обязателен при |
||
|
|
Нет |
|
|
Порт Redis при |
|
|
Нет |
Имя пользователя Redis при использовании Redis ACL. Для legacy |
||
|
|
Нет |
Пароль Redis при |
||
|
|
Нет |
|
Если |
|
|
|
Нет |
|
Если |
|
|
|
Нет |
|
|
Номер базы данных Redis при |
|
|
Нет |
|
|
Таймаут Redis в миллисекундах при |
|
|
Нет |
|
|
Keepalive timeout для Redis в миллисекундах при |
|
|
Нет |
|
|
Размер keepalive pool для Redis при |
|
|
Нет |
Список узлов Redis Cluster. Требуется минимум два адреса. Обязателен при |
||
|
|
Нет |
Имя Redis Cluster. Обязательно при |
||
|
|
Нет |
|
Если |
|
|
|
Нет |
|
Если |
Пример конфигурации. Route с limit-count разрешает 1 запрос за 30 секунд для каждого IP-адреса клиента.
{
"uri": "/get",
"plugins": {
"limit-count": {
"count": 1,
"time_window": 30,
"rejected_code": 429,
"key_type": "var",
"key": "remote_addr"
}
}
}
limit-conn
Что ограничивает. limit-conn ограничивает количество одновременных активных запросов.
Этот плагин смотрит не на частоту и не на количество запросов за период, а на то, сколько запросов прямо сейчас находится в обработке.
Как работает. Система ведёт счётчик активных запросов по выбранному признаку: например, по Consumer, IP-адресу или другому ключу.
Когда запрос начинается, счётчик увеличивается. Когда запрос завершён и ответ отправлен клиенту, счётчик уменьшается.
Если новый запрос приходит в момент, когда счётчик уже достиг лимита, система отклоняет этот запрос и не передаёт его в upstream.
Пример. Настроен лимит: не больше 5 одновременных запросов от одного клиента.
Клиент отправил 5 запросов, и все они ещё выполняются. Пока хотя бы один из них не завершится, шестой запрос будет отклонён.
Как только один из активных запросов завершится, счётчик уменьшится, и система снова сможет принять новый запрос от этого клиента.
Когда пригодится. limit-conn полезен для API, где запросы могут долго занимать соединения или ресурсы бэкенда:
-
загрузка больших файлов;
-
streaming-ответы;
-
long polling;
-
медленные клиенты;
-
операции с долгой обработкой;
-
WebSocket-соединения.
Такой лимит помогает защитить бэкенд не от большого числа коротких запросов, а от ситуаций, когда клиент удерживает слишком много одновременных обработок.
Атрибуты.
| Атрибут | Тип | Обяз. | По умолч. | Допустимые значения | Описание |
|---|---|---|---|---|---|
|
|
Нет |
|
Максимальное число одновременных запросов. Запросы сверх |
|
|
|
Нет |
|
Число превышающих одновременных запросов, которые можно задержать. Запросы сверх лимита отклоняются сразу. Обязателен, если не настроен |
|
|
|
Да |
|
Базовая задержка в секундах для запросов сверх |
|
|
|
Нет |
|
Если |
|
|
|
Нет |
Список правил connection limiting. Если задан, имеет приоритет над |
||
|
|
Да |
|
Максимальное число одновременных запросов. Может быть статическим числом или выражением переменной, например |
|
|
|
Да |
|
Число превышающих одновременных запросов, которые можно задержать. Может быть статическим числом или выражением переменной. |
|
|
|
Да |
Ключ для подсчёта запросов. Если ключ не существует, правило не выполняется. Интерпретируется как комбинация переменных. |
||
|
|
Нет |
var |
|
Тип |
|
|
Нет |
|
Признак, по которому считаются запросы. Для |
|
|
|
Нет |
|
TTL ключа Redis в секундах. Используется при |
|
|
|
Нет |
|
|
HTTP-код, который возвращается при отклонении запроса из-за превышения лимита. |
|
|
Нет |
|
Тело ответа, которое возвращается при отклонении запроса из-за превышения лимита. |
|
|
|
Нет |
|
Если |
|
|
|
Нет |
|
|
Политика хранения счётчика. |
|
|
Нет |
Адрес узла Redis. Обязателен при |
||
|
|
Нет |
|
|
Порт Redis при |
|
|
Нет |
Имя пользователя Redis при использовании Redis ACL. Для legacy |
||
|
|
Нет |
Пароль Redis при |
||
|
|
Нет |
|
Если |
|
|
|
Нет |
|
Если |
|
|
|
Нет |
|
|
Номер базы данных Redis при |
|
|
Нет |
|
|
Таймаут Redis в миллисекундах при |
|
|
Нет |
|
|
Keepalive timeout для Redis в миллисекундах при |
|
|
Нет |
|
|
Размер keepalive pool для Redis при |
|
|
Нет |
Список узлов Redis Cluster. Требуется минимум два адреса. Обязателен при |
||
|
|
Нет |
Имя Redis Cluster. Обязательно при |
||
|
|
Нет |
|
Если |
|
|
|
Нет |
|
Если |
Пример конфигурации. Route с limit-conn ограничивает число одновременных WebSocket-соединений и включает проксирование WebSocket.
{
"uri": "/.ws",
"limit_conn": {
"conn": 2,
"burst": 1,
"default_conn_delay": 0.1,
"key": "remote_addr",
"rejected_code": 429
},
"enable_websocket": true
}
header-count-limit
Что ограничивает. header-count-limit ограничивает максимально допустимое число HTTP-заголовков в запросе.
Как работает. Плагин считает заголовки входящего HTTP-запроса. Если их количество превышает max_headers, система не передаёт запрос в upstream — вместо этого он отклоняет запрос и возвращает клиенту 400 Bad Request.
Когда пригодится. Плагин полезен, когда нужно ограничить запросы с чрезмерным числом заголовков: например, такие бывают при ошибках клиента, некорректно настроенных интеграциях или попытках перегрузить обработку заголовков.
Атрибуты.
| Атрибут | Тип | Обяз. | По умолч. | Допустимые значения | Описание |
|---|---|---|---|---|---|
|
|
Да |
|
Максимально допустимое количество HTTP-заголовков в запросе. При превышении лимита запрос отклоняется с |
Пример конфигурации. Плагин header-count-limit разрешает не больше 10 HTTP-заголовков в запросе.
{
"max_headers": 10
}
Размер заголовков запроса через Nginx
Что ограничивает. Настройки Nginx ограничивают максимальный размер request line и каждого отдельного поля заголовка HTTP-запроса.
Как работает. Nginx проверяет request line и каждое поле заголовка по отдельности. Сначала он пытается принять каждое из этих значений в основной буфер, который задаётся параметром client_header_buffer_size.
Если каждое значение влезает в буфер, Nginx обрабатывает запрос дальше.
Если какое-то из значений не помещается в основной буфер, Nginx пытается принять его в большой буфер, который задаётся через large_client_header_buffers. Если значение помещается в один большой буфер, Nginx обрабатывает запрос дальше. Если нет, отклоняет запрос.
При отклонении запроса система возвращает клиенту:
-
414 Request-URI Too Large— если слишком длинная request line; -
400 Bad Request— если слишком длинное поле заголовка.
Где настраивается. Параметры задаются в конфигурационном файле conf/config.yaml в блоке nginx_config.http. Вы можете изменить эти настройки через CLI сервера, подключившись к нему по SSH.
Параметры.
| Параметр | Тип | Обяз. | По умолч. в Nginx | Пример значения | Описание |
|---|---|---|---|---|---|
|
|
Нет |
|
|
Размер основного буфера для чтения request line и полей заголовков запроса клиента. Если значение не поместится в основной буфер, Nginx попытается прочитать его через буфер, указанный в параметре |
|
|
Нет |
|
|
Максимальное число и размер больших буферов — то есть буферов для чтения больших request line и полей заголовков запроса клиента, которые не поместились в основной буфер. Запрос будет отклонён в любом из двух случаев:
|
Пример конфигурации. В этом примере request line или одно поле заголовка могут занимать до 32k.
nginx_config:
http:
client_header_buffer_size: 32k
large_client_header_buffers: 4 32k
Если request line превышает размер в 32k, клиент получает 414 Request-URI Too Large. Если поле заголовка запроса превышает размер в 32k, клиент получает 400 Bad Request.
Как выбрать плагин / механизм лимита
| Задача | Плагин / механизм лимита |
|---|---|
Ограничить RPS |
|
Задать 1000 запросов в минуту |
|
Задать дневную или месячную квоту |
|
Защититься от долгих активных запросов |
|
Сгладить всплески трафика |
|
Ограничить дорогой эндпоинт |
|
Ограничить одновременные downloads/streams/WebSocket |
|
Защититься от запросов с чрезмерным числом заголовков |
|
Защититься от запросов с аномально длинными URL или крупными заголовками |
настройки Nginx: |
Плагины часто комбинируют. Например, для одного Route можно настроить:
-
limit-count— не больше 10 000 запросов в день; -
limit-req— не больше 20 запросов в секунду; -
limit-conn— не больше 5 одновременных запросов; -
header-count-limit— не больше 50 HTTP-заголовков в запросе.
Если один из плагинов отклоняет запрос, запрос не передаётся в upstream. Остальные проверки дальше уже не имеют практического смысла для этого запроса.
Как подключить плагины
Через API
Плагины можно добавить к Route через Admin API. Для этого отправьте запрос на создание или обновление Route:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${admin_key}" \
-d '{
"id": "route-id",
"uri": "/example",
"plugins": {
"limit-count": {
"...": "ваши настройки плагина"
},
"limit-req": {
"...": "ваши настройки плагина"
},
"limit-conn": {
"...": "ваши настройки плагина"
},
"header-count-limit": {
"...": "ваши настройки плагина"
}
},
"upstream": {
"...": "ваши настройки upstream"
}
}'
Здесь id — идентификатор Route, uri — путь, по которому запрос попадает в Route, plugins.limit-count и другие объекты внутри plugins содержат конфигурацию плагинов, а upstream — настройки бэкенд-серверов.
Через UI
-
Откройте раздел Маршруты и нажмите
+ Добавить. -
В настройках маршрута (Route) перейдите в раздел Плагины.
-
Нажмите
+ Добавитьи выберите нужный плагин. -
Укажите конфигурацию плагина как JSON-объект.
-
Аналогично добавьте и настройте другие плагины, если нужно.
-
Сохраните созданный маршрут (Route). Он будет добавлен с подключённым плагином.
Что учитывать при настройке
-
Глобальный лимит должен соответствовать реальной ёмкости бэкенда. Если поставить слишком высокий лимит, бэкенд всё равно может перегрузиться. Если слишком низкий — легитимные клиенты будут получать ошибки.
-
Для публичных эндпоинтов без авторизации можно использовать лимит по IP, но не стоит считать его точной идентификацией пользователя.
-
Для клиентских квот лучше использовать лимит по Consumer: так лимит будет привязан к конкретному потребителю API, а не к сетевому адресу.
-
Для тяжёлых, долгих или потоковых запросов одного лимита по количеству запросов недостаточно. В таких случаях стоит добавить
limit-conn. -
При настройке лимита по количеству HTTP-заголовков учитывайте весь путь запроса; прокси, CDN, WAF и клиентские SDK могут добавлять собственные заголовки.
-
Для лимитов на request line и отдельные заголовки ориентируйтесь на реальные запросы: помните, что легитимные клиенты могут передавать длинные фильтры в query string, большие токены авторизации или крупные Cookie.