Подтверждение доставки ≠ бизнес-результат

  1. 01ПубликацияПродюсер → брокер
  2. 02ХранениеНадёжность записи
  3. 03ОбработкаБизнес-изменение
  4. 04ПодтверждениеВозможен повтор
Границу гарантий определяет весь путь, включая внешнюю базу или API.

У брокера сообщений нет переключателя reliable=true. Надёжность складывается из настроек продюсера, хранения в брокере, подтверждений потребителя и того, как приложение фиксирует бизнес-результат.

Ниже — обновлённый разбор RabbitMQ, ActiveMQ Classic и Kafka без обещаний универсального «ровно один раз».

Сначала разделим модели

RabbitMQ маршрутизирует сообщения через exchanges в очереди. Сообщение из очереди обычно обрабатывает один из конкурирующих потребителей. Если одно событие должны получить несколько систем, для них создают отдельные очереди и привязывают к exchange.

ActiveMQ Classic вырос вокруг JMS и поддерживает очереди, темы, постоянные подписки и несколько протоколов, включая OpenWire и AMQP. Конкретное поведение зависит от режима сохранения, типа сессии и подтверждений.

Kafka хранит записи в партиционированном логе. Чтение не удаляет запись: потребитель хранит позицию — offset — и при необходимости может перечитать историю. В классической consumer group одна партиция назначена одному потребителю в каждый момент; это не описание отдельного механизма share groups.

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

Что означают гарантии доставки

  • At-most-once. Сообщение может потеряться, зато повторная обработка не планируется. Так бывает, если подтвердить получение до завершения работы или использовать автоматическое подтверждение без возможности восстановления.
  • At-least-once. При сбое сообщение может быть доставлено повторно. Это обычный выбор для важных событий, но потребитель обязан переживать дубли.
  • Exactly-once. Это не свойство одного сетевого вызова. Нужна общая транзакционная граница между чтением, бизнес-эффектом и фиксацией позиции либо дедупликация на стороне получателя.

At-least-once действует в согласованной модели отказов и хранения. TTL, retention, удаление очереди, переполнение и политика DLQ могут прервать путь до успешной бизнес-обработки. Повторная доставка не означает бесконечную сохранность любого сообщения.

Фраза «брокер поддерживает exactly-once» без указания границ почти ничего не говорит. Важно уточнить: между какими компонентами, для каких операций и что произойдёт с внешней базой или API.

RabbitMQ: подтверждение не отменяет дубли

У RabbitMQ есть два независимых механизма:

  1. Publisher confirms сообщают продюсеру, что брокер обработал публикацию. Это не подтверждение обработки потребителем и не самостоятельная гарантия, что сообщение попало в нужную очередь.
  2. Consumer acknowledgements сообщают брокеру, что доставку можно удалить из очереди.

Для at-least-once потребитель отправляет ack только после успешного бизнес-действия. Если он записал результат в базу, но потерял соединение до ack, RabbitMQ вернёт сообщение в очередь. Следующая обработка увидит дубль — это нормальный сценарий, а не поломка брокера.

Успешный confirm возможен и для сообщения, которому exchange не нашёл ни одной очереди. Чтобы замечать такие случаи, для AMQP 0-9-1 используют mandatory и обрабатывают basic.return. Ожидание confirm без проверки маршрутизации может оставить продюсера довольным, а очередь — пустой.

Поэтому потребителю нужна идемпотентная операция или сохранённый идентификатор уже обработанного события. Флаг redelivered полезен для диагностики, но не заменяет корректную логику обработки.

Durable-очередь, persistent-сообщение, репликация и confirms закрывают разные участки пути. Их нельзя заменить одной галочкой.

ActiveMQ Classic: те же вопросы, другой стек

В ActiveMQ Classic надёжность зависит от persistent delivery, режима acknowledgement и транзакционной JMS-сессии. При rollback, разрыве соединения или незавершённой транзакции сообщение может быть доставлено повторно; после лимита попыток его обычно направляют в DLQ.

Транзакция внутри брокера полезна, но не делает атомарной запись в произвольную внешнюю систему. Если обработчик меняет отдельную базу данных, границу согласованности всё равно нужно проектировать явно.

Kafka: порядок только внутри партиции

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

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

Момент фиксации offset определяет поведение при сбое:

  • offset зафиксирован до бизнес-операции — возможна потеря обработки;
  • бизнес-операция выполнена до фиксации offset — возможен повтор;
  • результат и offset сохранены атомарно в одной системе — появляется более сильная гарантия в этой границе.

Идемпотентный producer Kafka защищает от части дублей при повторной отправке в Kafka. Транзакции Kafka позволяют атомарно записать выходные события и offsets в сценарии Kafka → обработка → Kafka. Для такого pipeline выходные записи и offsets должны фиксироваться одной транзакцией, автоматический commit offsets отключается, а следующий читатель использует isolation.level=read_committed, чтобы не видеть незавершённые и отменённые транзакции. Нужно также обработать abort, восстановление позиции и смену владельца партиции. Побочный эффект во внешней БД, платёжном шлюзе или HTTP API в эту транзакцию автоматически не входит.

Как выбирать брокер

Смотреть стоит не на таблицу «кто быстрее», а на задачу:

  • RabbitMQ удобен для очередей работ, сложной маршрутизации и независимых очередей подписчиков;
  • ActiveMQ Classic логичен в системах с JMS и уже сложившейся Java-интеграцией;
  • Kafka подходит, когда важны длительное хранение, повторное чтение, потоковая обработка и масштабирование по партициям.

Перед выбором я фиксирую семь вопросов:

  1. Можно ли потерять сообщение?
  2. Можно ли обработать его повторно?
  3. Как потребитель распознаёт дубль?
  4. Где подтверждается публикация, а где обработка?
  5. Нужен ли порядок и для какого ключа?
  6. Что делать с ошибочным сообщением после нескольких попыток?
  7. Как воспроизвести поток и проверить результат после инцидента?

Если на эти вопросы нет ответов, название брокера архитектуру не спасёт.

Официальная документация


Из моего Telegram-канала @it_underside.

Ваш ДПУПП

Похожие статьи