Что ломается без rate limiting в AI-сервисах
Генерация ответа моделью держит воркер занятым всё время стриминга — от первого токена до последнего. Когда клиент запускает пакетную обработку или на сервис приходит всплеск запросов от веб-клиента, параллельные вызовы выстраиваются в очередь, и каждый следующий ждёт слота дольше предыдущего.
Первым упирается пул воркеров: он рассчитан на среднюю нагрузку, а не на пиковую. Дальше растёт время ответа под нагрузкой — сначала до секунд, потом до таймаутов на стороне вызывающего. Очередь не бесконечна: при переполнении запросы отбрасываются, но часть работы уже выполнена и потрачена впустую.
Дальше начинаются каскадные отказы. Получив таймаут, клиент повторяет запрос, и повторные попытки добивают остатки ёмкости. Ошибки расползаются на соседние сервисы — входной шлюз, учёт потребления, поиск по индексам, кэш: все они получают шквал одинаковых повторов одновременно и деградируют вслед за основным.
Отдельная проблема — отсутствие разделения ёмкости. Один интегратор с агрессивным циклом опроса забирает весь пул и роняет качество работы для остальных, формально ничего не нарушая: перегрузка сервиса выглядит как общий сбой, а не как поведение конкретного клиента. Разбираем, как это закрывается: лимиты на клиента, квоты на токены и приоритеты очередей.
Что даёт управление лимитами: снятая нагрузка и отказы
Действие лимитов видно не по конфигурации, а по метрикам сервиса. Ниже — что меняется, когда для каждого ключа заданы пороги по запросам и токенам, а очередь ограничена по глубине.
Доля отказов перестаёт зависеть от чужого трафика
Без порогов всплеск у одного потребителя уходит в общий пул и роняет всех: сервис отвечает ошибками, повторные попытки добавляют нагрузки. Отдельная очередь на ключ обрывает эту цепочку — доля отказов держится в единицах процентов и в часы пика. Счётчик допуска ведём в Redis с накоплением токенов: проверка не удлиняет ответ модели.
Пик проходит деградацией, а не отказом
При десятикратном скачке часть запросов встаёт в очередь ограниченной глубины, остальные получают понятный отказ с указанием, когда повторить. Вместо лавины ошибок у всех — предсказуемая задержка для тех, кто прошёл порог. Устойчивость держится на том, что избыточный спрос переносится на клиента, а не на сервис.
Задержка ответа перестаёт «плавать»
Лимит на длину контекста и число одновременных генераций снимает борьбу за вычислительные ресурсы. Интерактивные запросы получают приоритет над пакетными, поэтому время ответа на 95-м процентиле остаётся в согласованном коридоре даже при полной загрузке.
Ёмкость распределяется равномерно
Квоты на команду и проект плюс отдельный пул для фоновых задач: один крупный потребитель не вытесняет остальных. Гарантированная доля ёмкости закреплена за каждым клиентом и не достаётся тому, кто просто отправил запрос первым.
Режимы ограничения: лимиты запросов, квоты токенов, очереди
Лимиты запросов, квоты токенов, параллельные сессии, буферизация и приоритеты ключей решают одну задачу — не дать одному потребителю выесть ёмкость модели. Гранулярность разная: rate limiting считает обращения, но не их вес, квота токенов не различает десять мелких вызовов и одно тяжёлое обращение с большим контекстом.
Поэтому режимы не выбирают «один из», а выстраивают в порядок: что проверяется первым, что уходит в очередь, кому достаётся остаток ёмкости.
| Вариант | Когда подходит | Чем ограничен |
|---|---|---|
| Лимит по запросам | Внешние интеграции с предсказуемым размером вызова | Не отражает стоимость: тяжёлый и короткий запрос считаются одинаково |
| Квота по токенам | Основной бюджет сценариев, где длина промпта и ответа плавает | Нужен точный учёт; при потоковой отдаче расход виден по факту |
| Параллельные сессии | Интерактивные интерфейсы и агенты, где важна отзывчивость | Ограничивает одновременность, а не суммарный объём |
| Буферизация запросов | Фоновые и пакетные задачи без требования немедленного ответа | Ответ приходит с задержкой, нужны таймаут и политика отказа |
| Приоритеты ключей | Несколько потребителей разной критичности на общей ёмкости | Нужен реестр ключей; неприоритетные получают отказ, а не ждут вечно |
На входе — приоритеты ключей, бюджет держим на квоте токенов, лимит запросов страхует от шквала мелких вызовов, параллельные сессии включаем для интерактива, буферизацию — для фоновых задач. Порядок проверок фиксируем в конфигурации, чтобы правила были воспроизводимы и не зависели от кода отдельного сервиса.
Этапы внедрения: от замера нагрузки до регламента
Прежде чем выставлять лимиты, мы снимаем реальный профиль: распределение запросов по часам, длину контекста, долю повторных вызовов, поведение отдельных клиентов. Пороги «на глаз» одинаково плохи в обе стороны — завышенный лимит не защищает от перегрузки, заниженный режет легитимные сценарии и выглядит для пользователя как нестабильность сервиса. Поэтому порядок работ жёсткий.
- Профиль нагрузки. Собираем статистику за несколько недель, включая хотя бы один пиковый период. Отдельно помечаем, какие обращения критичны для бизнес-процесса, а какие можно отложить в очередь.
- Расчёт порогов. Определяем лимиты послойно — на ключ, на пользователя, на весь сервис, — с запасом до насыщения модели, чтобы ограничитель срабатывал раньше, чем начнёт расти время ответа.
- Настройка ограничителя. Выбираем алгоритм под характер трафика (скользящее окно или пополняемое ведро) и вводим раздельные квоты на число запросов и на объём токенов: короткий вызов и длинный контекст расходуют ресурс несопоставимо.
- Обработка отказов. Ограничение должно быть предсказуемым: понятный код ответа, указание, когда повторить, приоритетная очередь для критичных операций.
- Проверка под пиком. Прогоняем профиль, повторяющий реальный пик с запасом, и фиксируем, на каких значениях появляются отказы и не задевают ли они добросовестных клиентов.
- Регламент эксплуатации. Передаём документ: как менять квоты, где смотреть метрики, при каких отклонениях поднимать лимит, как выдать временное исключение.
Отдельно договариваемся о пересмотре порогов — при смене сценариев или росте числа клиентов цифры устаревают быстрее, чем конфигурация.
Что передаём заказчику: конфигурации, схемы, регламенты
Отдаём не описание «как надо», а рабочий комплект, по которому дежурный инженер и разработчик работают без нас: файлы, применяемые прямо в контуре, и регламенты под конкретные инциденты.
- Конфигурация ограничителей — правила по ключу, пользователю и модели: окно, порог, вес запроса. Хранятся в репозитории, применяются на ходу, без перезапуска сервиса.
- Схема прохождения запроса — путь от клиента через шлюз до модели: где считается расход токенов, в какой точке срабатывает отказ и что возвращается вызывающей стороне.
- Матрица квот — таблица по ключам и группам клиентов: лимит в минуту и за сутки, отдельный потолок на дорогие модели, запас для служебных задач.
- Инструкция дежурной смены — как отличить всплеск нагрузки от злоупотребления, какие пороги смена поднимает сама, а какие только с согласованием.
- Журнал изменений порогов — кто, когда и по какой причине двигал лимит, с возможностью вернуть предыдущее значение.
- Метрики и оповещения — доля отказов, остаток квоты, время ожидания в очереди; сигнал приходит раньше, чем деградацию заметят клиенты.
- Порядок выдачи и ротации ключей — кому какой уровень доступа положен, как ключ отзывается и что происходит с уже запущенными запросами.
Комплект обновляем вместе с сервисом: иначе регламент расходится с контуром, и на инциденте никто не знает, где правда.
Кейс: пиковый наплыв пользователей без отказов
Пик у сервиса генерации ответов приходился на вечерние часы: 40 запросов в секунду при договорном лимите провайдера OpenAI в 20. Запросы уходили напрямую — без очереди, без учёта расхода токенов на пользователя и без разделения по тарифам. Каждый лишний запрос возвращал ошибку, клиент повторял его, и лавина только усиливалась.
Свою долю вносили и повторные отправки при таймауте: пользователь нажимал кнопку ещё раз, приложение ставило вторую задачу к тому же узкому месту. Итог — 7,8% ответов с ошибкой и p95 задержки 12 секунд, а в пиковые двадцать минут доля отказов доходила до 15%: люди уходили, не дождавшись ответа.
Что выставили на входе
Ограничение частоты сделали двухуровневым: token bucket на пользователя с правом на короткий всплеск и скользящее окно на 60 секунд для сглаживания. Сверху — суточная квота токенов по каждому тарифу и приоритетная очередь для платных обращений. Общий предохранитель на провайдера размыкается при росте ошибок и не даёт добить его повторами.
Повторные попытки перевели на экспоненциальную задержку с разбросом, а идентичные промпты в течение минуты стали отдаваться из кэша — это сняло часть нагрузки, которую раньше создавали сами пользователи.
Метрики после
- Отказы: 7,8% → 0,4% от всех запросов.
- Задержка p95: 12 секунд → 2,6 секунды; всплеск до 60 запросов в секунду проходит без деградации.
- Доля ответов из кэша в пике: 22%.
- Разбор накопившейся очереди после сбоя провайдера — 90 секунд вместо ручной перезаливки.
Квоты больше не роняют запрос молча: при исчерпании лимита система показывает расчётное время ожидания. Расход токенов стал планируемым по тарифам, а нагрузка на провайдера — предсказуемой.
Что делать, если AI-шлюз уже упирается в потолок?
Первым делом — замер в реальном времени, а не разбор логов недельной давности. На работающем сервисе включаем сбор метрик: доля отказов и повторных попыток, распределение запросов по ключам и моделям, время ответа на 95-м процентиле, глубина очереди. Ограничение скорости запросов и квоты по токенам пока не включаем — сначала нужно увидеть, кто именно создаёт нагрузку и где узкое место.
Дальше считаем теневые пороги. Правило применяется к каждому запросу мысленно: в журнал уходит вердикт, что было бы отклонено при такой квоте, но сам запрос проходит. Порог подбирается по реальному трафику, и сразу видна цена ошибки — скольких добросовестных клиентов задело бы ограничение.
Включение делаем поэтапным. Правило сначала действует на одном ключе или на небольшой доле трафика, затем доля растёт — у каждого шага свой критерий перехода. Раньше остальных закрываем редкие, но самые дорогие вызовы, и только потом общий поток.
Откат настроек живёт в конфигурации, а не в коде: профиль порогов меняется одной правкой и возвращается к предыдущему за секунды, без перезапуска и выката новой версии. Плюс автоматический откат — если доля отказов уходит выше базовой, правило снимается само.
Сервис не останавливается ни на одном из шагов. У заказчика остаётся журнал: какой порог действовал, сколько запросов затронул, когда его меняли — на этих данных строится следующая итерация.
Частые технические сомнения по лимитам и квотам
Ограничение частоты запросов и квоты токенов вводят не ради запретов: их задача — чтобы всплеск от одного клиента или зацикленный вызов не выедали ёмкость у остальных. Правила описываются декларативно, рядом со шлюзом, и бизнес-логика приложений остаётся нетронутой. Ниже — возражения, которые чаще всего поднимают на согласовании схемы: они про поведение реальных пользователей и про объём правок в коде.
Клиенты заметят, что их стали ограничивать?
Порог ставят выше пиковой нагрузки приложения, поэтому обычный пользователь его не видит: срабатывает только на аномалии — зацикленном запросе или парсере. В ответе клиент получает время повтора, а не тихую ошибку.
Можно ли задать разные правила для команд и продуктов?
Правила вешаются на ключ доступа, а не на сервис целиком: у внутренних команд, партнёрских интеграций и внешнего приложения пороги различаются. Квота токенов считается отдельно от частоты запросов.
Лимиты легко обойти?
Счётчики живут на стороне сервиса, а не в клиенте, поэтому правка кода приложения на них не влияет. Ключи выдаются и отзываются централизованно, аномалии по одному ключу видны в метриках.
Придётся ли переписывать код приложения?
Обязательной переписки нет: приложение обращается по тому же адресу, меняется только обработка ответа о превышении. При стандартных повторах правка сводится к нескольким строкам, её вносим в составе работ.
Обсудим вашу нагрузку — вернёмся с оценкой и планом
Чтобы выставить лимиты по расчёту, а не на глаз, хватает нескольких исходных данных — обычно они уже есть в логах и в статистике провайдера модели. Соберём картину и вернёмся с цифрами по квотам, срокам и границам работ.
- Профиль нагрузки: запросов в секунду в пике и в среднем, распределение по маршрутам API и по клиентам.
- Расход токенов: медиана и 95-й перцентиль на запрос и на ответ, доля длинных контекстов.
- Ключи и доступы: по чему считаем квоту — ключ, аккаунт, команда; как они выдаются и отзываются.
- Поведение при отказах: есть ли повторные отправки, пауза между ними, сколько трафика возвращается после кода 429.
- Что уже на входе: шлюз, прокси, балансировщик, где лежат счётчики — в памяти отдельного узла или в общем хранилище.
- Приоритеты: какие сценарии нельзя ронять, какие допустимо задержать в очередь.
Дальше готовим план: лимиты по запросам и по токенам, размеры окна и обработку всплесков, отдельные пулы для интерактивных и фоновых задач, поведение при исчерпании квоты — отказ с указанием времени ожидания, очередь или мягкая деградация. Возвращаем конфигурации шлюза, карту лимитов по ключам, порядок выката с обратным откатом, оценку сроков по этапам и зоны ответственности: что делаем мы, что остаётся на вашей стороне.
Опишите задачу и пришлите то, что есть по нагрузке, — вернёмся с оценкой и планом.







