Обратное давление в конвейерах: что ломается первым
Очередь в конвейере не спасает систему от перегрузки: она лишь переносит место отказа. Разбираем, как обратное давление проходит от медленного потребителя к продюсеру, какие лимиты срабатывают первыми и что измерять до аварии.

Когда потребитель обрабатывает 800 сообщений в секунду, а производитель отправляет 1 200, система не «работает с запасом»: она накапливает долг в очереди. Через некоторое время заканчивается память, заполняется диск, блокируется пул соединений или начинает тайм-аутить внешний сервис. Это и есть обратное давление — ситуация, в которой медленная часть конвейера ограничивает скорость всей цепочки.
Обратное давление начинается задолго до падения
Конвейер состоит из источника событий, одного или нескольких буферов и потребителей. У каждого звена есть пропускная способность. Если входной поток устойчиво превышает скорость обработки, длина очереди растёт по простой причине: сообщений приходит больше, чем успевает уйти.
Краткий всплеск пережить легко. Проблема начинается, когда разница между входом и обработкой держится дольше, чем позволяет буфер. В этот момент система должна выбрать одно из действий:
- замедлить производителя;
- увеличить число потребителей или их производительность;
- сохранить сообщения во внешнем хранилище;
- отбросить часть данных по заранее определённому правилу;
- продолжить принимать поток и дождаться отказа.
Последний вариант часто выбирается автоматически. Например, приложение продолжает читать запросы, пока брокер или локальная очередь не упирается в лимит. Поэтому «очередь есть» не означает «перегрузка обработана».
Обратное давление — это не отдельная ошибка брокера. Это контракт между звеньями: производитель обязан узнать, что потребитель больше не успевает.
Буфер1 — участок памяти или диска, где временно хранятся элементы потока между производителем и потребителем.
Что ломается первым
1. Память процесса
Самый быстрый сценарий — неограниченная очередь внутри приложения. Каждый непрочитанный элемент занимает память. Если сообщение содержит большой JSON, вложения или уже распарсенные объекты, несколько тысяч элементов могут занять больше, чем ожидает разработчик.
Сборщик мусора начинает работать чаще, растёт время пауз, а полезная обработка замедляется ещё сильнее. После этого процесс получает OOM Kill от операционной системы или контейнерного оркестратора. Внешне проблема выглядит как внезапный рестарт, хотя причина появилась раньше — в росте очереди.
2. Локальный диск и журналы
Если брокер сохраняет сообщения на диске, очередь может пережить всплеск дольше, но не бесконечно. Диск заполняется данными, журналами, временными файлами или ретраями. При исчерпании места брокер перестаёт принимать записи, а иногда не может корректно завершить операции записи.
Опасная деталь: метрика свободного места может выглядеть нормально, пока очередь растёт медленно. Важнее считать скорость роста backlog и прогнозировать время до лимита: ёмкость свободного диска делится на текущую скорость накопления.
3. Пулы соединений
Обработчик часто вызывает базу данных, HTTP-сервис или объектное хранилище. Когда внешняя зависимость отвечает медленнее, задачи дольше удерживают соединения. Пул исчерпывается, новые сообщения ждут свободный слот, а очередь продолжает расти.
Так возникает каскад: медленная база увеличивает время обработки сообщений, это увеличивает число одновременно выполняемых задач, а большее число задач усиливает нагрузку на базу. Масштабирование потребителей в такой момент может ускорить отказ, а не исправить его.
4. Тайм-ауты и повторные попытки
Тайм-аут сам по себе ограничивает ожидание, но не гарантирует безопасное восстановление. Если после каждого тайм-аута сообщение немедленно отправляется на повторную обработку, система получает retry storm — шторм повторных попыток.
Повторная задача конкурирует с новыми сообщениями за тот же пул потоков и подключения. Если внешний сервис уже перегружен, повторения увеличивают его нагрузку. Нужны ограничение числа попыток, задержка между ними и отдельный маршрут для сообщений, которые не удалось обработать.
Как распространяется давление по цепочке
Рассмотрим конвейер: HTTP-приёмник, брокер, воркеры, PostgreSQL и сервис уведомлений. Воркеры читают сообщения, записывают результат в PostgreSQL и отправляют уведомление. Если сервис уведомлений отвечает в пять раз медленнее, воркеры дольше удерживают контекст обработки. При этом возможны два разных результата.
- При ручном подтверждении сообщения брокер перестаёт выдавать новые сообщения после заполнения лимита неподтверждённых записей. Давление возвращается к чтению.
- При подтверждении до завершения побочных операций брокер считает работу законченной. При падении процесса часть уведомлений потеряется, а очередь будет выглядеть пустой.
Это ключевой архитектурный выбор: подтверждать сообщение после какого события. Чем раньше подтверждение, тем меньше давление на очередь, но выше риск потери незавершённой работы. Чем позже, тем лучше доставка, но больше занятых ресурсов и выше вероятность повторной обработки.
Какие стратегии действительно ограничивают поток
| Стратегия | Что ограничивает | Цена и риск | Когда применять |
|---|---|---|---|
| Ограниченный внутренний буфер | Память процесса | Производитель должен ждать или получать отказ | Для потоков внутри одного сервиса |
| Лимит чтения у потребителя | Число сообщений в работе | Меньше параллелизма, но стабильнее задержка | Когда обработка держит соединения и память |
| Пауза чтения из брокера | Скорость поступления в воркеры | Растёт backlog, нужен контроль возраста сообщений | При временной перегрузке зависимости |
| Ограничение на входе | Скорость производства событий | Клиент получает задержку или отказ | Когда потеря части запросов допустима по контракту |
| Сброс или выборочное отбрасывание | Размер очереди и стоимость хранения | Потеря данных; правило должно быть явным | Для событий, которые быстро теряют ценность |
Универсального режима нет. Для платёжной операции обычно важнее не потерять сообщение, чем сохранить минимальную задержку. Для телеметрии старые значения могут быть менее ценны, чем свежие. Это должно быть свойством бизнес-события, а не случайным эффектом настроек брокера.
Что измерять вместо одной длины очереди
Метрика queue depth показывает количество ожидающих сообщений, но не объясняет, успеет ли система восстановиться. Одинаковая очередь в 10 000 сообщений может быть безопасной для потребителя на 5 000 сообщений в секунду и критичной для потребителя на 50 сообщений в секунду.
Минимальный набор наблюдений для каждого этапа:
- скорость поступления и скорость успешной обработки;
- текущая длина backlog и скорость её изменения;
- возраст самого старого сообщения;
- время обработки: медиана и высокие перцентили, например p95 и p99;
- число активных, ожидающих и завершённых задач;
- ошибки, повторы, отложенные сообщения и сообщения в карантинном маршруте;
- память, CPU, место на диске и занятые соединения внешних зависимостей.
Критический сигнал — не только рост очереди, но и увеличение возраста старейшего сообщения. Если поток событий имеет срок годности, именно эта метрика показывает, когда обработка перестала приносить пользу.
Граничные случаи и место отказа
Очередь ограничена, но отказ не обработан
Когда буфер достигает лимита, библиотека может блокировать запись, возвращать ошибку или отбрасывать новые элементы. Эти режимы нельзя считать взаимозаменяемыми. Блокировка может занять HTTP-потоки, ошибка без корректного ответа приведёт к лавине повторов, а сброс незаметно ухудшит полноту данных.
Потребители масштабируются, но зависимость не выдерживает
Увеличение числа воркеров полезно, пока узким местом остаётся CPU или независимая обработка. Если все воркеры обращаются к одной базе с одним пулом соединений, дополнительная репликация только увеличит конкуренцию. Перед масштабированием измерьте время ожидания соединения и долю времени, которое задача проводит во внешнем вызове.
Сообщение нельзя обработать повторно без последствий
При позднем подтверждении возможны дубликаты: процесс мог записать результат, но упасть до подтверждения. Обработчик должен быть идемпотентным — повторное выполнение не меняет итог после первого успешного выполнения2. Если это невозможно, нужен отдельный механизм дедупликации и понятное хранилище состояния.
Идемпотентность2 — свойство операции, при котором повторное применение с теми же данными приводит к тому же результату, что и однократное.
Как проверять систему под нагрузкой
Не начинайте с увеличения числа сообщений в десять раз. Сначала зафиксируйте базовые условия: версию брокера, размер сообщения, число партиций или очередей, число потребителей, лимиты памяти, размер пула соединений и характеристики диска. Для Kafka 3.x отдельно учитывайте число партиций и распределение нагрузки между consumer group; для RabbitMQ 3.13 и 4.x — лимиты предварительной выдачи сообщений потребителю и режим подтверждений. Конкретные параметры зависят от версии и клиента.
Затем проведите три сценария:
- Устойчивый поток ниже расчётной производительности. Очередь должна оставаться около базового уровня, а возраст сообщений — не расти.
- Короткий пик с отключением или замедлением одной зависимости. Проверьте, срабатывает ли ограничение чтения и возвращается ли backlog к исходному уровню после восстановления.
- Длительная перегрузка. Зафиксируйте, что происходит при заполнении памяти, диска, пула соединений и ограниченного буфера.
Если замеров ещё нет, не подставляйте универсальные цифры пропускной способности. Запишите входную скорость, размер сообщений и время обработки на вашем стенде. Только после этого можно оценивать время накопления очереди и требуемое число потребителей.
Что делать дальше
Начните с карты конвейера: для каждого звена укажите лимит памяти, размер буфера, способ подтверждения, максимальное число параллельных задач и зависимость, которую оно вызывает. Затем добавьте алерты на скорость роста backlog, возраст старейшего сообщения, долю повторов и время ожидания соединений.
Следующий практический шаг — искусственно замедлить одну внешнюю зависимость и проверить, где появится первый отказ. Если система не умеет остановить чтение, безопасно отклонить вход или перевести сообщения в отдельный маршрут, обратное давление уже есть: пока оно просто не проявилось в проде.
Контент-конвейер: Из одной идеи — текст, изображение, озвучка и ролик. Один заказ вместо четырёх.
Спросить за 60 ₽Источники: Apache Kafka Documentation: Design, RabbitMQ Documentation: Consumer Prefetch
Частые вопросы
Нужно ли всегда увеличивать число потребителей, если растёт очередь?
Что выбрать: блокировать производителя или отбрасывать сообщения?
Что будет, если система не успеет обработать очередь вовремя?
Как понять, что проблема уже началась, хотя сервис ещё отвечает?
Подойдёт ли этот подход для другой версии Kafka или RabbitMQ?
Почему очередь растёт после увеличения числа воркеров?
Что важно проверить в проде после настройки обратного давления?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.