Мониторинг и кластеризация

Как за четыре числа понять, что брокеру плохо, — и что даёт кластер, кроме иллюзии надёжности.

Здоровье очереди определяется не её размером, а производной: растёт длина или падает. Очередь на 100 000 сообщений, которая уменьшается, — норма. Очередь на 300 сообщений, которая растёт третий час, — авария.

Четыре числа, которые надо знать наизусть

В панели RabbitMQ десятки метрик, но в 90% инцидентов ответ находится в этих четырёх.

МетрикаЧто значитО чём говорит рост
messages_readyЛежат в очереди, ждут потребителяПотребители не справляются или их нет вовсе
messages_unacknowledgedОтданы потребителю, но ack ещё не пришёлОбработчик завис. Если число равно prefetch × число потребителей и не меняется — потребитель мёртв, но соединение живо
publish rate / ack rateСообщений в секунду на входе и на выходеЕсли publish устойчиво больше ack — очередь будет расти вечно. Это единственное неравенство, которое надо отслеживать
consumersСколько потребителей подписаноНоль на рабочей очереди — самая частая причина «сообщения не доходят»

Из этих чисел выводится главный практический показатель — за сколько разгребётся затор. Считается элементарно, но именно этой цифры обычно не хватает в три часа ночи, когда надо решать, ждать или срочно поднимать потребителей.

ready = 128_400        # сообщений накопилось в очереди
publish_rate = 320.0   # приходит в секунду
ack_rate = 410.0       # обрабатывается в секунду

drain = ack_rate - publish_rate

if drain <= 0:
    print('Очередь не разбирается: потребители медленнее издателей.')
    print('Ждать бесполезно — нужно добавлять потребителей.')
else:
    seconds = ready / drain
    print(f'Чистая скорость разбора: {drain:.0f} сообщений/с')
    print(f'Очередь опустеет примерно через {seconds / 60:.1f} мин')

Результат:

Чистая скорость разбора: 90 сообщений/с
Очередь опустеет примерно через 23.8 мин

Поменяйте ack_rate на 300 — и увидите второй сценарий: разрыв не закроется никогда, сколько ни жди.

Где всё это смотреть

Четыре инструмента, от простого к боевому.

1. Панель управления. Включается плагином, живёт на порту 15672. Показывает очереди, скорости, соединения, каналы, позволяет заглянуть в сообщение (Get messages) и опубликовать тестовое. Незаменима при разборе инцидента, но для постоянного наблюдения не годится: истории она почти не хранит.

rabbitmq-plugins enable rabbitmq_management
# панель: http://localhost:15672 (по умолчанию guest/guest, только с localhost)

2. CLI. Первое, что запускают по ssh, когда «всё встало».

# Кто копит сообщения и есть ли у очередей потребители
rabbitmqctl list_queues name messages_ready messages_unacknowledged consumers

# Состояние кластера: кто в строю, кто отвалился
rabbitmq-diagnostics cluster_status

# Проверки для healthcheck в Kubernetes или Docker
rabbitmq-diagnostics check_running
rabbitmq-diagnostics check_port_connectivity

3. HTTP API. Тот же источник, что питает панель, — удобен для скриптов и самописных алертов.

# %2F — это URL-кодированный vhost "/"
curl -u guest:guest http://localhost:15672/api/queues/%2F/orders

4. Prometheus + Grafana. Единственный вариант для продакшена: история, графики, алерты. Плагин отдаёт метрики на порту 15692, официальные дашборды подключаются за десять минут.

rabbitmq-plugins enable rabbitmq_prometheus
# метрики: http://localhost:15692/metrics

Признаки беды

СимптомЧто произошлоЧто делать
ready растёт линейно, consumers = 0Потребители упали или не переподключились после разрываПоднять сервис, проверить логику reconnect
unacked застыл на числе prefetch × потребителиОбработчик завис: вечный запрос без таймаута, дедлок, внешний API не отвечаетПроставить таймауты на все внешние вызовы, перезапустить потребителя
Высокий redeliver rateСообщения ходят по кругу — тот самый poison messageНастроить DLQ и потолок попыток (урок про DLQ)
Растёт число соединений и каналовПриложение открывает соединение на каждое сообщениеДержать одно долгоживущее соединение и канал на поток
Издатели вдруг «зависли» на publishСработал memory или disk alarm — брокер включил flow controlСмотреть ниже

Алармы и flow control

У RabbitMQ есть предохранитель. Когда процесс съедает больше vm_memory_high_watermark (по умолчанию 40% оперативной памяти узла) или свободного места на диске остаётся меньше disk_free_limit, брокер поднимает alarm и перестаёт принимать публикации: соединения издателей получают connection.blocked, их basic_publish просто перестаёт возвращать управление.

Со стороны это выглядит как загадочное зависание веб-приложения без единой ошибки в логах. Поэтому запомните связку: «издатели встали намертво, ошибок нет» = смотри алармы брокера. Причина почти всегда одна — очередь, которую никто не разбирал, доросла до потолка памяти. Круг замыкается: именно от этого спасают лимиты и TTL из прошлого урока.

Кластер: зачем и как

Кластер RabbitMQ — это несколько узлов, которые видят друг друга и делят общие метаданные: пользователей, vhost'ы, обменники, определения очередей. Клиент может подключиться к любому узлу и увидеть одну и ту же картину.

И здесь главное недоразумение новичков. Обычная (classic) очередь живёт целиком на одном узле — том, где её создали. Метаданные разошлись по кластеру, а сами сообщения — нет. Падает узел-владелец — очередь становится недоступна вместе со всем содержимым, хотя кластер формально жив. Кластер сам по себе не даёт отказоустойчивости данных.

Даёт её quorum queue — очередь, реплицированная по алгоритму консенсуса Raft. Тип задаётся при объявлении:

ch.queue_declare(
    queue='orders',
    durable=True,
    arguments={
        'x-queue-type': 'quorum',              # вместо classic
        'x-quorum-initial-group-size': 3,      # три реплики на трёх узлах
        'x-delivery-limit': 5,                 # 5 неудачных доставок → DLX
        'x-dead-letter-exchange': 'orders.dlx',
    },
)

Что здесь важно:

  • Число узлов — нечётное: 3 или 5. Raft требует большинства (для трёх узлов — двух). Кластер из двух узлов бесполезен: потеря одного — потеря большинства.
  • x-delivery-limit — та самая четвёртая дверь в DLX. Quorum queue сама считает доставки в заголовке x-delivery-count и после лимита отправляет сообщение в карантин. Бесконечный цикл переотправки, о котором мы говорили в первом уроке, здесь предотвращён на уровне брокера, без единой строчки кода.
  • Тип очереди нельзя сменить ни policy, ни повторным объявлением — только пересоздать. Решайте на старте.
  • Quorum queues платят за надёжность: они всегда durable, пишут на диск на всех репликах и не поддерживают часть возможностей classic-очередей. Для потока телеметрии, который не жалко, classic-очередь честнее и быстрее.

Как это работает

В quorum queue у каждой очереди есть лидер и последователи. Все операции идут через лидера; сообщение считается принятым, только когда его записало большинство реплик — для группы из трёх это две. Упал лидер — оставшиеся двое выбирают нового, клиенты переподключаются, данные на месте. Упали двое из трёх — большинства нет, очередь замирает и отказывает в записи. Это не баг, а осознанный выбор: лучше отказать, чем разъехаться на две расходящиеся копии.

Отсюда же правило про сетевые разделы (split brain). Если сеть рвётся пополам, RabbitMQ по умолчанию должен решить, кому жить. Рекомендуемая стратегия — pause_minority: узлы, оказавшиеся в меньшинстве, добровольно приостанавливаются и не принимают клиентов, пока связь не восстановится.

cluster_partition_handling = pause_minority

И последнее, о чём молчат туториалы: кластер не растягивают через интернет. Raft чувствителен к задержкам, узлы должны стоять в одном датацентре с быстрой сетью между ними. Для связи между регионами есть отдельные инструменты — плагины Shovel и Federation, которые асинхронно перекачивают сообщения между независимыми брокерами.

Частые ошибки

  • Мониторить только размер очереди. Число само по себе ничего не значит — алертить надо на устойчивый рост и на «нет потребителей», а не на абсолютную длину.
  • Кластер из двух узлов. Не даёт кворума. Три — минимум, имеющий смысл.
  • Считать, что кластер = отказоустойчивость. Classic-очередь на упавшем узле недоступна, кластер здесь ничем не помогает. Нужны quorum queues.
  • Игнорировать unacked. Растущий unacked при пустом ready — это тихо умирающие потребители, которых панель показывает живыми.
  • Открывать соединение на каждое сообщение. TCP-handshake плюс AMQP-handshake на каждую публикацию: сотни соединений, упёршийся лимит файловых дескрипторов, лежащий брокер.
  • Не проверять healthcheck перед выкаткой. rabbitmq-diagnostics check_running в контейнере стоит две минуты настройки и экономит часы.

Итоги

  • Следите за производной, а не за размером: publish rate устойчиво выше ack rate — очередь не разгребётся сама никогда.
  • Застывший unacked, равный prefetch × потребители, — верный признак зависшего обработчика.
  • Панель на 15672 — для разбора инцидента, Prometheus на 15692 — для постоянного наблюдения и алертов.
  • Внезапно «зависшие» издатели без ошибок — почти всегда memory или disk alarm и включённый flow control.
  • Кластер делит метаданные, но classic-очередь живёт на одном узле; отказоустойчивость данных даёт только quorum queue.
  • Quorum queues — нечётное число узлов (3 или 5), x-delivery-limit вместо самописного счётчика попыток, тип очереди назад не поменять.
  • Кластер — в пределах датацентра; между регионами — Shovel и Federation.
Проверьте себя
1. В панели видно: messages_ready = 0, messages_unacknowledged = 40 и не меняется уже полчаса, consumers = 2, prefetch_count = 20. Что происходит?
AВсё в порядке: сообщения обрабатываются, unacked — это нормальный рабочий буфер
BОчередь переполнена и брокер включил flow control
CОба потребителя забрали по полной пачке prefetch и зависли — сообщения взяты, но ack не приходит
DИздатели перестали публиковать сообщения
2. У вас кластер RabbitMQ из трёх узлов и обычные (classic) очереди. Узел, на котором была создана очередь orders, упал. Что произойдёт с очередью?
AНичего: кластер автоматически реплицирует все очереди между узлами
BОчередь станет недоступна вместе с сообщениями — classic queue живёт целиком на одном узле
CОчередь автоматически превратится в quorum queue и продолжит работать
DСообщения уйдут в DLX соседнего узла