← назад к разделу

Протокол AMQP — это базовая модель. На проде поверх неё живёт много операционных решений: что выбрать между Classic / Quorum / Streams, как кластеризовать, как делать репликацию между датацентрами, как мониторить, какие лимиты на одну ноду. Senior-разработчик не обязательно настраивает кластер руками, но обязан знать, что под капотом — иначе невозможно проектировать сервис, который не упадёт первой проблемой.

Кластеризация

RabbitMQ-кластер — это группа узлов, у которых одинаковые metadata (определения exchange, queue, binding, users) и которые знают друг о друге. Метаданные синхронизируются через Raft (с 3.10+) или Mnesia (старее).

                ┌──────────────────────────┐
client A ──────►│  Node 1  (queue Q1)      │
                │          (queue-leader)  │◄── replication ──┐
                └────────────┬─────────────┘                  │
                             │                                │
                ┌────────────┴─────────────┐    ┌─────────────┴────────────┐
client B ──────►│  Node 2  (queue Q1 рез.) │◄──►│  Node 3 (queue Q1 рез.)  │
                └──────────────────────────┘    └──────────────────────────┘

Важно понять: сама queue физически живёт на одном узле (Classic) или реплицируется по нескольким (Quorum / Streams). Узел, где queue была объявлена, становится её «домом». Соединение клиента может прийти на любой узел кластера — если запрашиваемая queue не на этом узле, узел проксирует.

Из этого вытекают практические следствия:

  • Перебой одного узла = недоступна часть queue, у которых он был «домом», пока узел не вернётся (Classic) или не пройдёт failover (Quorum).
  • Сетевая партиция (split brain) — серьёзная проблема. RabbitMQ умеет несколько стратегий (pause-minority, autoheal, ignore), но в любой из них без боли не обходится. Решается выбором правильного типа queue.
  • Минимум 3 узла для кворумных решений. С 2 узлами при сбое одного второй уходит в read-only (pause-minority) — и кластер не работает.

Три типа queue

Classic Queue (legacy)

Стандартный тип с самого начала RabbitMQ. По умолчанию — не реплицирована: живёт на одном узле, на других в кластере её нет.

Раньше можно было включить classic mirroring через политику ha-mode: all|exactly|nodes — queue зеркалилась на N других узлов. Этот механизм deprecated в 3.8 и полностью удалён в 4.0. Использовать в новом коде нельзя.

Когда брать Classic Queue сейчас:

  • Внутри одного узла — для временных queue (RPC-ответы, broadcast'ы, кэш-инвалидации).
  • Если допустима потеря: метрики, неважные уведомления.
  • Совсем не подходит: бизнес-события, заказы, платежи.

Quorum Queue (стандарт для бизнес-данных)

Введены в 3.8, стандарт с 3.10. Реализованы через Raft: каждая queue живёт на N узлах (обычно 3 или 5), один из них — leader, остальные — followers. Запись подтверждается, когда большинство реплик её получили.

# объявление quorum queue
channel.queue_declare(
    queue="orders",
    durable=True,
    arguments={"x-queue-type": "quorum"}
)

Свойства:

  • Высокая доступность — потеря 1 узла из 3 не влияет; потеря 2 — queue недоступна до возврата.
  • Сильная durability — каждое сообщение реплицировано на большинство до подтверждения publisher confirm.
  • Дороже Classic — больше I/O, больше памяти. На небольшие очереди — overkill; на критичные — обязательно.
  • Не поддерживают: x-message-ttl (есть, но с оговорками), priority queues, exclusive queues, transient messages.

Quorum Queues — выбор по умолчанию для всего, что важнее «можно потерять».

Streams (с 3.9, для append-only сценариев)

Появились в 3.9 как реакция на конкуренцию Kafka. Это append-only лог, реплицированный через Raft, с поддержкой повторного чтения по offset:

channel.queue_declare(
    queue="events-log",
    durable=True,
    arguments={
        "x-queue-type": "stream",
        "x-max-length-bytes": 10_000_000_000  # 10 GB ретеншна
    }
)

Подходит, когда:

  • Нужна возможность переиграть (replay): новый сервис подписался на старые события за неделю.
  • Многократное чтение одного и того же сообщения разными потребителями без копирования.
  • Высокий throughput (миллионы сообщений в секунду).

Не подходит, когда нужны: routing по headers/topic-паттернам, RPC, dead-letter routing.

Streams — компромисс «давайте сделаем Kafka внутри RabbitMQ». Если задача чисто log-based и нужен throughput Kafka-уровня — обычно лучше Kafka. Streams хороши, когда уже есть RabbitMQ-инфраструктура и хочется log-сценарий не плодя ещё одну систему.

Persistence и lazy queue

Persistent сообщение в Classic/Quorum queue пишется на диск. Но в памяти оно тоже держится — для быстрой доставки потребителю.

Lazy queue — режим Classic queue, где сообщения сразу пишутся на диск и не держатся в памяти. Применялось, когда queue могла стать огромной (миллионы сообщений). С Quorum queues необходимость исчезла — в них поведение настраивается через x-max-in-memory-length.

В новых проектах: используем Quorum + настройку x-max-in-memory-length под workload.

Federation и Shovel — между датацентрами

Кластеризация RabbitMQ не подходит для далёких узлов (>10 мс латентности). Raft требует low-latency-связи внутри кластера; cross-region кластеризация ведёт к нестабильности и split brain.

Для гео-репликации — отдельные кластеры в каждом регионе + перенос сообщений между ними:

  • Federation — связь exchange-to-exchange или queue-to-queue между кластерами. Сообщения публикуются в exchange/queue одного кластера и асинхронно копируются в соответствующий exchange/queue в другом. Decoupled, кластеры не зависят друг от друга.
  • Shovel — простой «насос» между двумя точками: читает из source queue, публикует в destination exchange. Гибче, чем federation, проще в отладке. Лучше для одноразовых миграций и сценариев точка-точка.

Сравнение с Kafka: там cross-DC решается через MirrorMaker 2 / Confluent Replicator — концептуально похоже на federation.

Backpressure и flow control

Когда queue растёт быстрее, чем обрабатывается, RabbitMQ применяет flow control:

  1. Block / unblock publish — продьюсера ставит на паузу, если queue или весь брокер достигли лимита памяти/диска (vm_memory_high_watermark, disk_free_limit).
  2. Credit-based flow control между узлами кластера — задержка распространения внутри.
  3. Prefetch на стороне консьюмера (см. протокол).

Когда сработал block — публикация тормозится синхронно. Сервис, который писал «fire and forget», вдруг получает таймауты или повышенную латентность. Это правильное поведение брокера, но приложение обязано уметь работать с ним: backpressure до клиента, ограничение по запросам, fallback.

Мониторинг

Что мониторить (через RabbitMQ Management UI / Prometheus exporter):

МетрикаЧто значитАлерт
rabbitmq_queue_messages_readyСообщений ждёт обработки> N + растёт
rabbitmq_queue_messages_unackedВыдано консьюмерам без ack> prefetch × consumers × 2
rabbitmq_node_mem_used / rabbitmq_node_mem_limitRAM> 80%
rabbitmq_node_disk_freeСвободное место< 1 GB
rabbitmq_channel_messages_unackedPer-channel unackedСильный рост = висит обработка
rabbitmq_queue_consumersЧисло активных консьюмеров0 при ожидаемом ≥ 1
rabbitmq_connectionsОткрытые соединенияВнезапный спайк или ноль

Главное — мониторить ratio messages_ready vs consumer throughput. Это аналог Kafka consumer lag.

Sizing — сколько чего

Грубые ориентиры для одного узла современного железа (8 cores, 32 GB RAM, NVMe):

  • Throughput: 30–50 тыс. сообщений/сек для Quorum Queues с persistence, 100+ тыс/сек для Classic non-persistent.
  • Размер сообщения: оптимум 1–10 KB. Сообщения >100 KB сильно ломают throughput, >1 MB вообще лучше не возить через брокер (положить в S3 + переслать ссылку).
  • Число очередей на узле: тысячи — норма, десятки тысяч — нагрузка на metadata, сотни тысяч — рискованно.
  • Соединения: тысячи нормально, десятки тысяч — нужен socket_writer-тюнинг.

Сравнение: Kafka на том же железе тянет 500K–1M+ сообщений/сек, но при значительно большем размере хранилища и более жёсткой topology.

Disaster recovery

Базовая стратегия для критичного сервиса на RabbitMQ:

  1. Кластер из 3+ узлов в одном датацентре, Quorum Queues для бизнес-сообщений.
  2. Federation/Shovel в backup-кластер в другом датацентре для exchange/queue с долгой ретенцией.
  3. Резервная копия конфигурацииrabbitmqctl export_definitions, в git.
  4. Restore-сценарий протестирован, не «придумаем когда упадёт».

При полном падении основного кластера: переключаем приложение на backup, теряем максимум federation lag сообщений (обычно секунды).

Что почитать дальше

  • Протокол AMQP — модель доставки, без операционных деталей.
  • Spring AMQP — клиентский код.
  • Messaging-паттерны через AMQP — work queue, pub/sub, RPC.
  • AMQP vs Kafka — когда брать что.
  • RabbitMQ: Quorum Queues — официальная документация.