Протокол 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:
- Block / unblock publish — продьюсера ставит на паузу, если queue или весь брокер достигли лимита памяти/диска (
vm_memory_high_watermark,disk_free_limit). - Credit-based flow control между узлами кластера — задержка распространения внутри.
- 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_limit | RAM | > 80% |
rabbitmq_node_disk_free | Свободное место | < 1 GB |
rabbitmq_channel_messages_unacked | Per-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:
- Кластер из 3+ узлов в одном датацентре, Quorum Queues для бизнес-сообщений.
- Federation/Shovel в backup-кластер в другом датацентре для exchange/queue с долгой ретенцией.
- Резервная копия конфигурации —
rabbitmqctl export_definitions, в git. - Restore-сценарий протестирован, не «придумаем когда упадёт».
При полном падении основного кластера: переключаем приложение на backup, теряем максимум federation lag сообщений (обычно секунды).
Что почитать дальше
- Протокол AMQP — модель доставки, без операционных деталей.
- Spring AMQP — клиентский код.
- Messaging-паттерны через AMQP — work queue, pub/sub, RPC.
- AMQP vs Kafka — когда брать что.
- RabbitMQ: Quorum Queues — официальная документация.