Модуль 4. RabbitMQ Streams и multi-DC: shovel, federation и когда это вместо Kafka

Цель модуля: закрыть курс инженерной надстройкой над AMQP: RabbitMQ Streams — лог-модель с replay и офсетами внутри RabbitMQ; shovel и federation — связь кластеров; и честное решение «Streams или Kafka».

Перед началом: Модули 1–3, курс «Фундамент» (модели доставки), знакомство с Kafka из её курса — для сравнения. Трудоёмкость: ~8–10 ч (теория 4 ч · практика 4 ч · квиз 1 ч).


📖 4.1. RabbitMQ Streams: лог внутри Rabbit

Обычная очередь удаляет сообщение после ack — «до ack» и точка. Stream — это append-only лог: сообщения лежат на диске до retention (по времени/размеру), потребители читают с офсетом и могут перечитать сколько угодно раз, несколько независимых читателей не конкурируют за сообщения:

bash
rabbitmqadmin declare queue name=orders kind=stream \
  arguments='{"x-stream-max-length-bytes": 5000000000, "x-stream-max-segment-size-bytes": 500000000}'
classic/quorumstream
Жизнь сообщениядо ackдо retention (дни, если нужно)
Повторное чтениенетда, с любого офсета
ПорядокFIFO, но конкуренция за головустрогий, как Kafka
Консьюмерыконкурентные (сообщение уходит одному)независимые группы с офсетами
Throughputсотни тысяч msg/sмиллионы msg/s на узел (протокол оптимизирован)

Ключевое отличие от Kafka: нет партиций — stream живёт на кластере целиком (реплики), порядок глобальный в стриме, масштабирование — шардированием стримов руками (как в Redis, Модуль 5 курса Redis). Протокол — отдельный (порт 5552, плагин rabbitmq_stream), клиент — rstream-go; записывать можно и по AMQP 0-9-1 — в стрим это обычная публикация.

Когда stream, а не очередь: нужны replay/аудит/догон новых потребителей, несколько независимых читателей одного потока, retention задолго за пределы обработки — и при этом уже стоит RabbitMQ.


📖 4.2. Чтение с офсетом и группы

Stream-потребитель подписывается с указанием точки входа:

  • first — с самого начала (replay!);
  • last — только новое;
  • offset — конкретный офсет / timestamp («всё с 14:00»);
  • consumer group: несколько читателей делят офсет-коммит группы (как consumer group Kafka) — для масштабирования обработки.
go
// rstream-go (эскиз)
env := rabbitstream.NewEnvironment(...)
offset := rabbitstream.NewOffsetType(rabbitstream.OffsetFirst) // replay
reader, _ := env.NewConsumer("orders", handler,
  rabbitstream.ConsumerOffset(offset), rabbitstream.ConsumerName("billing"))

Подтверждение — офсет-коммит группы: тоже at-least-once, идемпотентный обработчик из «Фундамента» снова в деле. Клиент держит heartbeat и переподключение — как всегда, «живость» решает consumer_timeout.


📖 4.3. Shovel: перекачка между брокерами

Shovel — встроенный переносчик: читает из одной очереди/стрима (свой брокер, vhost, очередь) и публикует в другой, с подтверждениями на обоих концах. Конфигурируется динамически (parameters):

bash
rabbitmqctl set_parameter shovel orders-drain \
  '{"src-uri":"amqp://src","src-queue":"orders", \
    "dest-uri":"amqp://dest","dest-queue":"orders", \
    "ack-mode":"on-confirm","prefetch-count":100}'

Применения: миграция кластеров без остановки (dual-write через shovel), Active-Passive DR (стендайби догоняется постоянно), перенос истории из legacy. Semantics: ack-mode=on-confirm — at-least-once на обоих концах; идемпотентность получателя — ваша (снова «Фундамент»).


📖 4.4. Federation: гео и развязка кластеров

Federation — постоянная связь exchange↔exchange или queue↔queue между разными кластерами через AMQP-линки: upstream тянет сообщения к локальному exchange как обычный клиент. Отличия от shovel:

ShovelFederation
Тип связиточка-точка, переносдолгоживущие линки, подписка
Топологиягибкая, параметризуемаядекларативная (upstream-наборы)
Классика применениямиграция, DRActive-Active, гео-агрегация

Federation не реплицирует гарантии кластера: это at-least-once поверх AMQP между независимыми брокерами (у каждого свои очереди, своя глубина). Для денег и строгих гарантий — cross-DC Kafka (или отдельная архитектура), federation — для развязки доменов и сбора событий из регионов.


📖 4.5. Multi-DC шаблон выбора

  • Active-Active (federation): оба ДЦ живут, события текут в обе стороны; очереди независимы; дубли — норма, идемпотентность обязательна;
  • Active-Passive (shovel/репликация на стендайби): один ДЦ пишет, второй догоняется; failover — ручной/полуавтоматический, RPO зависит от задержки shovel;
  • Ничего из этого: если нужен один лог с сильными гарантиями по ДЦ — это отдельный разговор (Kafka MirrorMaker / BookKeeper-класс решений), а не RabbitMQ-надстройки.

📖 4.6. Streams RabbitMQ vs Kafka: короткое решение

Берите RabbitMQ Streams, если: RabbitMQ уже стоит, объёмы до сотен тысяч msg/s, нужна replay-история на дни, topology/маршрутизация AMQP рядом, и не хочется ещё одного кластера в эксплуатации. Берите Kafka, если: retention на недели/месяцы, партиции и масштабирование записи, экосистема (Connect/Streams), жёсткие throughput-требования, много независимых consumer-групп на один лог. Формулировка для ревью: «Streams — лог внутри RabbitMQ; Kafka — лог, вокруг которого построена инфраструктура».


💻 Практика

  1. Включите Streams: make up-streams (плагин + порт 5552); создайте stream orders с retention 1 ГБ.
  2. Запись и replay: продюсер AMQP пишет 1 000 событий; два независимых читателя rstream-go читают first — оба получают все 1 000 (вот оно, непривычное для AMQP); перезапуск читателя с offset=last — только новые.
  3. Группа: два читателя в одной группе — офсет общий, работа делится; остановите одного, второй доезжает.
  4. Shovel (демо-миграция): создайте orders (queue) и orders-v2 (stream); shovel перекачивает очередь в стрим; гоняйте трафик и убедитесь в сохранности по счётчику ID.
  5. Federation (скетч): два стенд-кластера (docker network), exchange-federation events из A в B; напишите в A — прочитайте в B; отключите A — B продолжает жить (независимость кластеров).

Артефакт: скрипты двух читателей (replay + группа), конфиг shovel, скриншот «оба читателя получили все сообщения» — приклад к ADR «streams vs kafka» из курса Redis (М6).


🧩 Quiz "Проверь себя"

1. Кому streams RabbitMQ проигрывают почти всегда?
2. Federation между ДЦ — это…
3. Shovel с ack-mode=on-confirm гарантирует…
4. Чем stream отличается от quorum-очереди принципиально?

✅ Итоги модуля

ТемаГлавное
StreamsЛог внутри Rabbit: retention, replay, офсеты, группы; нет партиций
Клиентrstream-go, порт 5552; запись можно по AMQP
ShovelПеренос между брокерами, at-least-once, миграции и DR
FederationЛинки между кластерами: Active-Active, гео, независимость
Решение«Streams — лог внутри Rabbit; Kafka — лог со инфраструктурой вокруг»

🎓 Куда дальше

Курс RabbitMQ закрыт по тройке «модель → мониторинг → инциденты» плюс надстройка Streams/multi-DC. Осталось в треке: капстоун OrderFlow — там Streams одного из брокеров, shovel-миграция между спринтами и все инцидент-навыки трека соберутся в один проект.