Здравствуйте, меня зовут Александр, я backend-разработчик. В данной публикации я хочу рассказать про относительно новый брокер сообщений NATS (Neural Autonomic Transport System). А также сравнить его с более популярными и известными брокерами RabbitMQ и Apache Kafka.
В упрощенном виде определение брокера сообщений звучит следующим образом — это программное обеспечение, которое управляет обменом сообщениями между различными компонентами распределенной системы. Нет «волшебного», универсального брокера. При выборе оптимального брокера необходимо отталкиваться от архитектуры приложения и от тех кейсов, нагрузок с которыми будет работать приложение. Кроме того, никто не запрещает при необходимости использовать несколько брокеров сообщений в одном приложении.
Перед тем, как приступить к обсуждению NATS, в вкратце рассмотрим основные концепции и особенности работы брокеров Kafka и RabbitMQ.
Apache Kafka
Можно дать несколько определений брокера Kafka – это распределенная, масштабируемая система обмена сообщений с высокой пропускной способностью или распределенный журнал сообщений, который действует как брокер сообщений. Брокеры работают в кластере, обеспечивая высокую доступность, отказоустойчивость и распределенную обработку данных.
Принципы:
PULL – consumer сам забирает сообщения
Тупой брокер – умный consumer
Publish – subscribe (публикация - подписка)
Особенности Kafka:
Все данные физически хранятся на жестких дисках брокеров и сообщения при чтении не удаляются.
producer может писать в несколько брокеров
Нет подтверждения доставки, в случае ошибки на стороне consumers, логипа повторного чтения должна быть реализована в consumer. Брокер не проверяет обработано сообщение или нет
Посдедовательная запись данных на жесткий диск. Журнал kafka используется только для добавления данных в конец файла.
Read with zero copy - Оптимизированная передача данных потребителю по сети.
Процесс чтения данных с диска брокера без использования zero copy:
данные с диска -> Буфер ОС -> Приложение Kafka -> socket буфер -> Буфер сетевой платы (NIC) -> Consumer
С использованием нулевой копии:
данные с диска -> Буфер ОС -> Буфер сетевой платы (NIC) -> Consumer
Таким образом, Apache Kafka представляет из мощный себя инструмент, ориентированный на высокую пропускную способность с сохранением истории сообщений. Но при этом основная сложность ложится на consumer и отсутствует прямая возможность отправить request / reply запрос к конкретному сервису.
Но отправку request / reply можно реализовать с помощью дополнительных библиотек Kafka Streams или Kafka Connect. Также, хотя Kafka оптимизирована для высокой пропускной способности, она может иметь более высокую латентность по сравнению с другими брокерами сообщений. При этом Kafka может потреблять значительные ресурсы, такие как память и дисковое пространство, что может быть избыточным для небольших объемов данных.
RabbitMQ
Это брокер сообщений, основанный на протоколе AMQP (Advanced Message Queuing Protocol), который служит посредником для обмена информацией между различными системами посредством очередей.
Принципы:
PUSH –RabbitMQ «проталкивает» сообщения потребителю. Для этого потребителю необходимо установить соединение, и он должен быть подписан на текущую очередь. При этом задача роутинга сообщений ложится на Брокера.
Умный брокер – Тупой консьюмер
Producer – consumer
Все входящие сообщения от сервисов, прежде чем попасть в очереди, публикуются в точке обмена «Точка обмена 1» — это единая точка в рассматриваемой топологии. Дальше брокер осуществляет их маршрутизацию: направить сообщение в какую-либо очередь или направить его в другие точки обмена («Точка обмена 2»). Существует несколько типов точек обмена:
Direct - Прямая отправка сообщений в одну или несколько очередей с совпадающим значением ключа маршрутизации
Fanout - Все сообщения отправляются во все очереди независимо от ключа маршрутизации
Headers - Маршрутизация по нескольким атрибутам, заданным в заголовке сообщения. Ключ маршрутизации игнорируется
Topic - Сообщение отправляется в конкретные очереди по значению ключа маршрутизации, заданного по шаблону
Есть три типа очередей: обычная очередь: с обеспечением гарантий консистентности; и начиная с версии RabbitMQ новый вид – Stream, представляющий из себя аналог принципов Apache Kafka и NATS. Более подробно про типы очередей и их свойства.
Особенности RabbitMQ:
Данные могут физически хранятся на диске, и есть подтверждения доставки
Проверяет обработано сообщение или нет, если да, то удаляет его, иначе отправляет повторно
Для изменения параметров Exchange нужно его удалять и декларировать заново.
Exchange не подразумевает хранения! Это не очередь. Если маршрут для сообщения не будет найден, сообщение сразу будет отброшено без возможности его восстановления.
Flow controll – при накоплении очереди, понижается скорость публикации сообщений
Таким образом, RabbitMQ представляет из себя инструмент, ориентированный гибкую маршрутизацию сообщений между различными сервисами. Он часто используется для микросервисной архитектуры, где он выступает средством связи между приложениями. Но возможность построения такой сложной маршрутизации увеличивает сложность его конфигурации, значительно снижает производительность, особенно при обработке очень больших объемов данных.
NATS CORE / NATS JStream
RabbitMQ позволяет настраивать сложную и многоуровневую маршрутизацию, но за это приходится платить скорость, kafka хранит все данные, даже если это и не требуется, и для реализации модели обмена сообщениями request/reply требуется отдельная библиотека.
Но в случае, если архитектура приложения не требует такой сложной маршрутизации и нет огромного объема входных данных, как в случае с realtime сервисами, для организации микросервисной архитектуры стоит рассмотреть брокер NATS.
NATS – это система очередей сообщений, которая появилась в 2010, написана на языке Go. NATS хорошо подходит для передачи сообщений в режиме реального времени. На сегодняшний день в NATS JetStream реализована долговечность и гарантированная доставка сообщений. NATS поддерживает публикацию/подписку (pub/sub), запрос/ответ (request/reply) и другие модели обмена сообщениями.
Брокер NATS поддерживает механизм кластеризации. Каждый сервер может работать как отдельный узел или быть частью кластера. В кластере NATS один из серверов выбирается в качестве лидера, а остальные серверы работают как реплики. Лидер управляет состоянием кластера и координирует работу реплик. Если один сервер выходит из строя, остальные серверы продолжают работать, обеспечивая непрерывную доставку сообщений. Кластер NATS легко масштабируется горизонтально, добавляя больше серверов для обработки увеличивающегося объема сообщений. Это позволяет поддерживать высокую производительность и низкую задержку при росте нагрузки.
NATS состоит из NATS CORE и NATS JStream. NATS CORE - базовый набор функциональных возможностей (pub / sub, request / reply). NATS JStream (персистентный слой NATS) - распределенная система сохранения данных, работает поверх NATS CORE. позволяет хранить сообщения на диске или в ОП, для отложенной доставки.publish/ subscribe.
Запустить NATS JStream можно командой: nats server run –jetstream
Потоковая передача через JStream идеальна, когда:
Требуется историческая запись потока. Это когда потребителю требуется воспроизведение данных.
Для инициализации требуется последнее сообщение, созданное в потоке, и производитель может быть отключен.
Производители и потребители данных сильно разобщены. Они могут подключаться в разное время, и потребители должны получать сообщения или приложения должны использовать данные в своем собственном темпе.
Требуется раздельное управление потоком между издателями и потребителями потока
streams (потоки) можно создавать в nats_cli, terraform, или клиентские библиотеки.
publish/ subscribe
Продюсер отправляет сообщение по теме, и любой активный подписчик, слушающий эту тему, получает сообщение. subjects – топики (orders.* и т.д.) Это что-то вроде связки exchange-queue в RabbitMQ (изображение).
Сообщения состоят из: Тема + Полезная нагрузка в виде массива байтов + Любое количество полей заголовка + Необязательное поле адреса "ответить"
Сообщения имеют максимальный размер (который устанавливается в конфигурации сервера с помощью max_payload). По умолчанию размер сообщения равен 1 МБ, но при необходимости его можно увеличить до 64 МБ (хотя мы рекомендуем сохранить максимальный размер сообщения на более разумном уровне, например 8 МБ).
К примеру размер сообщение, timestamp и т.д.
wildcard позволяет подписаться на несколько топиков, по шаблону:
один уровень топиков. hello.*
– для всех вида hello.<текст>
, вида hello.<текст>.<текст>
- не получит сообщение
любое число уровней топика. hello.>
для всех вида hello.<любой текст>
, hello.<текст>.<текст>
- тоже получит сообщение
Это позволяет фильтровать сообщение на стороне NATS. Т.е. можно создать один поток с большим количеством данных чем несколько разных потоков.
request / reply
request / reply - обычная схема в современных распределенных системах. Отправляется запрос, и приложение либо ожидает ответа с определенным таймаутом, либо получает ответ асинхронно (изображение).
NATS поддерживает шаблон Запрос-ответ, используя свой основной механизм связи — публикацию и подписку. Запрос публикуется по заданной теме с использованием темы ответа. Респонденты прослушивают эту тему и отправляют ответы теме ответа.
Несколько ответчиков NATS могут формировать динамические группы очередей. Следовательно, нет необходимости вручную добавлять или удалять подписчиков из группы, чтобы они могли начать или прекратить рассылку сообщений. Это делается автоматически. Это позволяет респондентам увеличивать или уменьшать масштаб в соответствии с требованиями. Возможности NATS позволяют получать несколько ответов, когда используется первый ответ, а система эффективно отбрасывает дополнительные.
Группы очередей в NATS
Когда подписчики регистрируются для получения сообщений от издателя, схема обмена сообщениями 1: N гарантирует, что любое сообщение, отправленное издателем, достигнет всех подписчиков, которые зарегистрировались.
NATS предоставляет дополнительную функцию под названием "очередь", которая позволяет подписчикам регистрировать себя как часть очереди. Подписчики, входящие в очередь, образуют "группу очереди".
Подтверждающие сообщения NATS
Некоторым пользователям требуется, чтобы код клиентского приложения подтверждал обработку или потребление сообщения, но существует более одного способа подтвердить (или не подтвердить) сообщение
Ack Потребитель подтверждает сервер NATS, что сообщение было полностью обработано. Можно подтверждать как отдельное сообщение так и группу сообщений, как показано на следующем рисунке. Подтверждения являются ассинхронные.
Можно указывать максимальное число сообщений которые можно принять до подтверждения – параметр max_ack_pendingNak Сигнализирует, что сообщение не будет обработано сейчас и обработка может перейти к следующему сообщению, после чего сообщение будет отправлено повторно. Можно указать через какой интервал времени оно будет повторно отправлено.
InProgress Отправленные до истечения периода ожидания, указывают на то, что работа продолжается и период должен быть продлен еще на один, равный AckWait
Term Инструктирует сервер прекратить повторную доставку сообщения без подтверждения того, что оно успешно обработано
Event Sourcing: Consumer подтверждает каждое сообщение явно (Ack Explicit). Это означает, что потребитель отправляет подтверждение обработки каждого сообщения отдельно.
Reporting: Consumer подтверждает все сообщения сразу (Ack All). Это означает, что потребитель отправляет подтверждение обработки всех сообщений одновременно.
Lookup: Consumer подтверждает только последнее сообщение в субъекте (Ack Last). Это означает, что потребитель отправляет подтверждение обработки только последнего сообщения в определенном субъекте.
Распределенное Хранилище ключ-значения и файлов.
Еще одной особенностью NATS является возможность создавать распределенное хранилище ключей / значений (аналог Redis). Это выполняется командой nats kv
.
Хранилище файлов позволяет клиентским приложениям создавать buckets (соответствующие потоки), которые могут хранить набор файлов. Файлы хранятся и передаются порциями, что позволяет безопасно передавать файлы произвольного размера по инфраструктуре NATS. При этом хранилище файлов не является распределенной системой хранения. Все файлы в корзине должны помещаться в целевую файловую систему.
Особенности хранилища NATS:
Возможность отслеживания изменений, происходящих с одним ключом, что аналогично подписке на ключ: наблюдатель получает обновления из-за операций ввода или удаления с ключом, переданных ему в режиме реального времени по мере их выполнения
Возможность отслеживания изменений, происходящих со всеми ключами в корзине
Возможность извлекает историю значений (и операций удаления), связанных с каждым ключом, с течением времени.
Таким образом, хранилище ключ-значения NATS можно использовать для кеширования данных, с возможностью отслеживать и реагировать на их изменение или удаление.
Сравнение брокеров
Небольшое сравнение рассмотренных брокеров сообщений (данные взяты в том числе с сайта):
|
Kafka |
RabbitMQ |
NATS |
Год выпуска |
2011 |
2007 |
2014 |
Язык |
Java / Scala |
Erlang |
Go |
Паттерны |
Publish – subscribe Балансировка нагрузки с помощью групп потребителей |
producer – consumer, Publish – subscribe в stream, Балансировка нагрузки с помощью рабочей очереди.
|
producer - consumer и load-balanced queue subscriber. |
Удаление сообщений |
Сообщения не удаляются |
Сообщения удаляются после подтверждения |
Сообщения удаляются после подтверждения |
Персистентность |
Есть |
Нет, есть в stream |
Есть |
Подтверждение доставки |
Нет |
Есть |
Есть, но не обязательна |
Гарантии доставки |
По крайней мере один раз, ровно один раз. |
Не более одного раза, по крайней мере один раз. |
не более одного раза, по крайней мере один раз, ровно один раз |
Роутинг сообщений |
Нет |
Разветвленный роутинг сообщений, приоритеты |
Точка - Точка |
мультитенантность*1 |
Multi-tenancy не поддерживается. |
Multi-tenancy поддерживается с помощью vhosts |
Multi-tenancy поддерживается |
Сохранение и персистентность сообщений |
сохранение на основе файлов. Сообщения можно воспроизводить, указав смещение |
сохранение данных на основе файлов. Rabbit поддерживает семантику на основе очереди, поэтому воспроизведение сообщений недоступно. |
сохранение в памяти, файлах, базах данных. Сообщения могут воспроизводиться по времени, количеству или порядковому номеру |
Высокая доступность и отказоустойчивость |
Полностью реплицированные участники кластера координируются через Zookeeper. |
Поддержка кластеризации с полной репликацией данных с помощью плагинов |
Поддержка кластеризации, есть серверы резервного копирования, поддерживает горизонтальную масштабируемость |
Горизонтальная масштабируемость |
Есть |
Нет (нужны плагины) |
Есть |
Мониторинг |
Ряд инструментов управления и консолей, включая Confluent Control Center, Kafka, веб-консоль Kafka, монитор смещения Kafka. |
CLI tools, система управления на основе плагинов с информационными панелями и инструментами сторонних производителей. |
NATS поддерживает экспорт данных мониторинга в Prometheus (Prometheus Nats Exporter) |
Пропускная способность*2 |
До 2 миллионов сообщений в секунду |
До 60 тысяч сообщений в секунду |
До 3 миллионов сообщений в секунду |
1* мультитенантность — это возможность изолированно обслуживать пользователей из разных организацией (т.е. независимых подписчиков SaaS) в рамках одного сервиса (одной инсталляции или развертывания).
2* Взято с сайта
Заключение:
NATS это легковесная, простая и надежная система обмена сообщениями. Это делает NATS возможным выбором для систем обмена сообщениями в различных сценариях использования. У NATS активное сообщество разработчиков, хорошая документация и периодические обновления.
Легковесная архитектура NATS, минимальная конфигурация, поддержка зеркальных очередей и персистентного хранилища делают его подходящим для использования в контейнеризированных средах и микросервисах, а также для приложений, требующих высокой надежности и безопасности. Низкие требования к ресурсам делают его подходящим для использования в облачных средах и на устройствах с ограниченными ресурсами.
NATS официально поддерживает следующие языки программирования и существуют библиотеки для интеграции: Go, Java, JavaScript/Node.js, Python, Ruby, C#/.NET, C, Swift.
Таким образом, брокер NATS является достойным аналогом для других, более известных брокеров.
Комментарии (7)
parus-lead
21.01.2025 12:20Интересный разбор, детали и сравнения.
Скажите пожалуйста, у Вас был большой опыт работы с этим брокером? И насколько Вы лично довольны NATS?aleksandr__sokol Автор
21.01.2025 12:20Добрый день!
Лично я работал с более года с NATS (до этого с Kafka)
Мне очень понравился этот брокер. Довольно быстрый брокер, с багами при его работе я не сталкивался.
olku
21.01.2025 12:20Полезный обзор. Для полноты картины в сравнительную таблицу хорошо бы ActiveMQ добавить.
dph
21.01.2025 12:20Не совсем корректно говорить, что в Kafka нет подтверждения доставки. Как раз гарантии доставки в Кафка есть, сообщение будет доставлено до consumer (если таковой вообще существует). Нет гарантии обработки, но их нет и в RabbitMQ и, насколько знаю, в NATS.
Возможно, стоило бы еще и сказать про реальную призводительность и надежность, где Kafka много лучше и RabbitMQ и NATS.
Гарантий доставки exactly once не существует. В Kafka так называется автоматическая дедупликация между producer и broker, но фактически consumer может читать данные много раз. В NATS, подозреваю, так же (так как exactly once физически нереализуемо в распределенных систем).
Ну и, насколько я понимаю, в таблице 3 млн. событий в секунду для NATS указано для режима без persistance, что делает эти числа бесполезными.
P.S. Текст выглядит как автоматический перевод. Если это так, то стоит указать оригинальный источник. Если нет, то стоит вычитать и поправить грамматические ошибки, их очень много.
alhimik45
Не скажу за NATS Core, но JetStream выглядит как сырая технология. Соседняя команда завязалась на его. Потом вышло так что его кластер разваливался по любому поводу, причём стейт корраптился так, что чинилось только удалением всего стейта. С single node не сильно лучше получается, на большой нагрузке вылезают баги. А на гитхабе не сильно-то и помогают с нестабильно воспроизводящимися багами. Предагают купить поддержку у компании-разработчика за дофига денег и тогда они может и посмотрят.
aleksandr__sokol Автор
Добрый день!
Может Ваши коллеги работали на более старых версиях NATS?
Я работал на версии 2.9.15 и nats JetStream работал стабильно
alhimik45
Нет, работают наоборот на самых последних релизах, так как это единственная бесплатная рекомендация в ишуях гитхаба. Кроме случая когда в релизе 2.10.2x (не помню точно) была регрессия которая ломала ещё больше. И такое успели поймать в последнее время