3 типа очередей в RabbitMQ: чем они отличаются и когда что использовать?

RabbitMQ для аналитика, проектирование потокового конвейера на RabbitMQ, асинхронная интеграция сообщений, брокеры сообщений примеры курсы обчение

Какие параметры и аргументы очереди RabbitMQ отвечают за отказоустойчивость потокового конвейера на основе этого брокера сообщений: сходства и отличия кворумных очередей с классическими, а также как кролик косит под Kafka с версии 3.9 благодаря потоковым очередям.

Классические и кворумные очереди в RabbitMQ

Одним из наиболее ярких отличий RabbitMQ от Apache Kafka, считается невозможность повторного считывания сообщения, т.к. брокер удаляет его из очереди после того, как потребитель подтвердил получение данных. Однако, это характерно только для классических очередей RabbitMQ. Помимо них, в этом брокере сообщений также есть еще стриминговые (потоковые) и кворумные очереди, которые отличаются от классических.

Вообще очередью в RabbitMQ называется структура данных на диске или в памяти, которая хранит ссылки на сообщения и отдает их копии одному или нескольким потребителям. По своей сути, очередь – это stateful-процесс Erlang, на котором написан RabbitMQ. Долговечная (durable) очередь сохраняет свое состояние и восстанавливается после перезапуска брокера, а временная (transient), как и следует из ее названия, недолговечна и хранится в памяти.

Если очередь используется только одним соединением и удаляется при его закрытии, она является эксклюзивной (exclusive): к ней может подключаться только один потребитель и она всегда временная. Управлять количеством очередей можно динамически, установив очереди параметр autodelete=true, чтобы она удалялась автоматически, когда ее последний потребитель будет отменен или исчезнет из-за закрытия/сбоя канала или соединения. Однако, такая динамичность повышает потребление вычислительных ресурсов на сервере и снижает производительность брокера.

Сегодня временные неэксклюзивные классические очереди считаются устаревшими – вместо них рекомендуется использовать долговременные очереди или временные эксклюзивные очереди. Это снижает риски случайного удаления очереди, когда один из клиентов-потребителей отключился, а остальные ещё работают. При автоматическом удалении очереди сообщения теряются и нет гарантии, что все клиенты получат данные. Наконец, отсутствие эксклюзивности увеличивает сложность управления очередью и повышает вероятность возникновения состояния гонки между потребителями. Поэтому временные неэксклюзивные очереди не соответствуют современным подходам к проектированию надежных систем обмена сообщениями. Их заменяют долговременными очередями для совместного доступа к надежно сохраняемым данным или временными эксклюзивными для частных сессий и RPC-ответов.

Однако, помимо параметров долговечности, эксклюзивности и автоудаления, у очереди RabbitMQ есть аргумент типа, который может принимать следующие значения:

  • Classic – ранее упомянутые классические очереди, когда сообщения хранятся в оперативной памяти, но при ее переполнении могут сбрасываться на диск. Впрочем, даже на диске хранение данных недолговременное, т.к. после получения данных потребителем брокер удаляет сообщение из этой очереди. Классическая очередь хранит данных в виде FIFO-структуры без репликации, имеет высокую скорость публикации с толерантностью к потерям данных. Классические очереди RabbitMQ хорошо работают на небольших объемах: до 100 000 сообщений в очереди или до нескольких сотен мегабайт (не более 1-2 ГБ). При превышении этих лимитов скорость работы падает, особенно для операций чтения и удаления данных, а также растет потребление ресурсов, что чревато рисками перегрузки брокера и его сбоя.
  • Quorum – кворумные очереди с репликацией данных по узлам кластера. Они построены на базе алгоритма консенсуса Raft: каждая очередь представляет собой группу из нечетного числа реплик, т.е. кворум. Решения о записи/чтении принимаются на основании кворума, т.е. большинства реплик, а не их всех. Распределенное хранение сообщений повышает отказоустойчивость потоковой системы. Кроме того, в отличие от классической, сообщения из кворумной очереди удаляются не сразу после получения подтверждения от потребителя. Когда потребитель получает сообщение из кворумной очереди и подтверждает его получение, т.е. отправляет ack, сообщение помечается как доставленное и подтверждённое. Но физически оно продолжает храниться в очереди, пока не выполнится пройдёт сборки мусора (garbage collection). Этот системный процесс RabbitMQ запускает периодически, чтобы очистить сообщения, которые уже были подтверждены всеми необходимыми репликами (узлами-кворума) и больше не нужны для восстановления состояния очереди. Таким образом, надежность кворумных очередей выше, чем у классических, т.к. они позволяют гарантировать целостность данных даже в случае сбоев. Разумеется, производительность кворумных очередей нже, чем у классических, а задержка обработки данных – выше. Это особенно заметно на больших нагрузках: десятки тысяч сообщений в секунду, множество одновременных клиентских подключений и большой размер самих сообщений (сотни килобайт или мегабайты). Однако, четкий хронологический FIFO-порядок сообщений сохраняется даже при сбоях.

Таким образом, классические очереди RabbitMQ подходят для сценариев, где скорость важнее надежности, например, очередь множества однотипных задач, где потеря нескольких некритична, отправка push-уведомлений, обмен промежуточными данных между внутренними сервисами. В свою очередь, кворумные очереди стоит использовать, когда требуется высокая надежность и отказоустойчивость данных, когда события нельзя потерять или там, где повторная отправка сообщения может вызвать дублирование транзакций. Например, денежные переводы, заказы клиентов, обработка платежей. Этот вариант позволит пережить отказ узла в кластере или разделение сети, обеспечив доставку сообщений при сбоях оборудования или перезапуске брокеров.

Однако, это еще не все! С версии 3.9 в RabbitMQ появились еще потоковые очереди (Stream), которые делают этот брокер сообщений немного похожим на Apache Kafka. Об этом поговорим далее.

Как кролик косит под Kafka: потоковые очереди

Потоковые или стриминговые очереди в RabbitMQ похожи на распределенные AO-логи (append-only log) как в Apache Kafka. Они позволяют хранить миллионы и даже миллиарды сообщений прямо внутри очереди, поддерживают одновременное чтение одного и того же сообщения разными потребителями, а также допускают повторное потребление сообщений. Горизонтальное масштабирование обеспечивается благодаря шардированию очереди по сегментам (шардам). Одна очередь как логическая сущность разбивается на несколько независимых шардов, каждый из которых хранит часть сообщений. Каждому шарду назначается определённый диапазон сообщений. Шарды могут размещаться на разных узлах кластера RabbitMQ. При публикации новых сообщений в очередь они распределяются между разными шардами согласно алгоритму шардирования, например, по хэшу ключа сообщения или простым перебором (Robin-Round). Потребители могут читать сообщения параллельно из разных шардов, что увеличивает производительность и позволяет масштабировать обработку нагрузки.

Потоковая очередь по умолчанию создаётся как долговременная (durable). Сообщения в потоковой очереди всегда долговременно сохраняются на диск, даже если ее параметр durable = false. Длина потоковой очереди по умолчанию не ограничена, но на практике рекомендуется указывать лимиты, чтобы избежать переполнения диска. Потоковые очереди не поддерживают приоритет сообщений и потребителей — эти функции доступны только для классических очередей.

Кроме того, стриминговые очереди используют специальный бинарный протокол RabbitMQ Streams, а не AMQP 0.9.1. Поэтому для работы с ними нужен отдельный клиент или специальные плагины.

Потоковые очереди подходят для сценариев потоковой обработки данных, реализации паттерна Event Sourcing, аналитических задач и асинхронной интеграции приложений, когда данные нужно хранить долго и читать их несколькими группами потребителей.

При работе с потоковыми очередями в RabbitMQ реализуются следующие концепции:

  • Поток – AO-лог, откуда не удаляются потребленные сообщения. Потоки в RabbitMQ сохраняются и реплицируются. Это обеспечивает безопасность и доступность данных при потере узла, а также масштабируемость: можно читать один и тот же поток с разных узлов. По своей сути, это похоже на раздел топика Kafka.
  • Суперпоток — логический поток из отдельных обычных потоков. Это способ масштабирования публикации и потребления: большой логический поток делится на потоки разделов, разделяя хранилище и трафик на несколько узлов кластера. По своей сути, это похоже на сам топик Kafka.

Возникает логичный вопрос: превращают ли потоковые очереди RabbitMQ в Kafka? Об этом расскажу в новой статье.

Обо всем этом и других технологиях проектирования архитектуры и интеграции информационных систем вы узнаете на моих курсах Школы прикладного бизнес-анализа и проектирования информационных систем в нашем лицензированном учебном центре обучения и повышения квалификации системных и бизнес-аналитиков в Москве:

 

Я даю свое согласие на обработку персональных данных и соглашаюсь с политикой конфиденциальности.