Серия RabbitMQ (десять) распределенных RabbitMQ (кластер)

RabbitMQ

Обзор

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

В этом руководстве рассматриваются основные темы кластеризации RabbitMQ:
1. Как идентифицируются узлы RabbitMQ: имя узла;
2. Кластерные требования;
3. Какие данные могут реплицироваться между узлами кластера, а какие нет;
4. Что кластер означает для клиента;
5. Как формируются кластеры;
6. Как проверить обе стороны между узлами (включая инструменты CLI);
7. Перезапуск узла и как вернуться в кластер;
8. Как сбросить узлы кластера;
Более...
Формирование кластера и обнаружение пировтесно связанные руководящие принципы; Информацию о репликации содержимого очереди (сообщения) см.зеркальная очередьгид;

Среди них метаданные включают:

  1. метаданные виртуального хоста;
  2. переключать метаданные;
  3. метаданные очереди;
  4. связывание метаданных;
  5. пользовательские метаданные;
  6. метаданные разрешений;

Уведомление:

  1. Сообщения в очереди и очереди находятся только на узле сервера (например, узле 1), который их создал, а другие узлы хранят только свои метаданные и указатель узла сервера, где находится очередь выполнения;
  2. Когда клиент напрямую подключается к узлу сервера (например, узлу 1), где расположена очередь, и публикует или подписывается на очередь, сообщения в кластере относятся только к узлу сервера (например, узлу 1), где находится очередь. расположен;
  3. Когда клиент подключается к узлу сервера, не входящего в очередь (например, узлу 2 или 3), и выполняет операции публикации сообщений или подписки в очереди, полные данные очереди не находятся на этих двух узлах (например, узле 2 или 3). , то это Два узла в основном играют роль в маршрутизации и пересылке.Согласно метаданным на этих двух узлах, они перенаправляются на узел сервера, где находится очередь (например, узел 1), и окончательное отправленное сообщение все равно будет храниться на узле сервера, где расположена очередь (например, на узле 1). Точно так же, если потребитель сообщений подключен к серверному узлу, не входящему в очередь (например, к узлу 2 или 3), то эти два узла также будут действовать как узлы маршрутизации для пересылки и будут отправлены с узла сервера, на котором расположена очередь. (например, узел 1) Извлекать сообщения для потребления.
  4. Обычный режим кластера не гарантирует высокую доступность очереди. Хотя коммутаторы и привязки могут быть реплицированы на любой узел в кластере, содержимое очереди не реплицируется. Хотя этот режим устраняет нагрузку на узел, время простоя узла очереди напрямую приводит к тому, что очередь не может быть применена и может только ждать перезапуска.
    Следовательно, чтобы иметь возможность нормально применять даже при неработающем или неисправном узле очереди, содержимое очереди необходимо скопировать на каждый узел в кластере, то есть его необходимо создатьзеркальная очередь.

Формирование кластеров

Как формируются кластеры

Кластер RabbitMQ можно сформировать несколькими способами:
1) Объявлен перечислением узлов кластера в конфигурационном файле;
2) Заявлено с помощью обнаружения на основе DNS;
3) Объявлено (через плагин) с помощью обнаружения экземпляров AWS (EC2);
4) Объявлено с помощью обнаружения Kubernetes (через плагин);
5) путем объявления (через плагины) на основе Consul discovery;
6) заявляя (через плагины) на основе обнаружения etcd;
7) Ручной инструмент rabbitmqctl;
Подробную информацию см. в руководстве по формированию кластера.

Состав кластера может динамически изменяться. Все брокеры RabbitMQ изначально работают на одном узле. Эти узлы могут присоединяться к кластеру и повторно выходить из него как независимые брокеры сообщений.

Имя узла (идентификация)

Узлы RabbitMQ идентифицируются по имени узла. Имя узла состоит из двух частей: префикса и имени хоста. Например, rabbit@node1.messaging.svc.local, префикс — Rabbit, а имя хоста — node1.messaging.svc.local;
Когда узел запускается, он проверяет, присвоено ли ему имя узла. По умолчанию он будет использовать префикс «rabbit» и имя хоста, на котором он находится, в качестве имени узла.

  1. В кластере узлы или инструменты CLI идентифицируются и связываются с ними по имени узла. Это означает, что имя хоста каждого узла должно быть разрешимым;
  2. Имена узлов должны быть уникальными в кластере. Если на хосте запущено более одного узла (обычно в среде разработки или контроля качества), они должны использовать разные префиксы. Например, rabbit1@hostname и rabbit2@hostname;
  3. Пользовательское имя узла:
    Настраивается с помощью переменной среды RABBITMQ_NODENAME; если система использует полное доменное имя в качестве имени хоста, узлы RabbitMQ и инструменты CLI должны быть настроены на использование длинного имени узла. Для сервисных узлов этого можно добиться, установив для переменной среды RABBITMQ_USE_LONGNAME значение true. Для инструментов CLI можно установить RABBITMQ_USE_LONGNAME или указать параметр --longnames.

Требования к формированию кластера

разрешение имени хоста

Узлы RabbitMQ используют доменные имена (короткие или полностью определенные) для обнаружения друг друга. Поэтому имена хостов всех членов кластера должны быть разрешимыми, а машины в кластере могут использовать инструменты командной строки, такие как rabbitmqctl.
Разрешение хоста может использовать любой метод, предоставляемый стандартной операционной системой: записи DNS для файлов локальных хостов, таких как /etc/hosts.

В более строгих средах, где изменение записи DNS и файла хоста ограничено, невозможно или нежелательно, виртуальная машина Erlang может быть настроена на использование альтернативных методов разрешения имен хостов, таких как альтернативные DNS-серверы, локальные файлы, нестандартные хосты Расположение файла или комбинация методов . Эти методы работают со стандартными методами разрешения имен хостов ОС.

доступ к порту

Узлы службы RabbitMQ связывают порты (открытые сокеты TCP сервера), чтобы получать соединения от клиентов, инструментов CLI и других узлов RabbitMQ. Другие процессы и инструменты (например, SELinux) или брандмауэры могут препятствовать привязке RabbitMQ к порту. Если это произойдет, узел не запустится. Поэтому убедитесь, что следующие порты доступны:

порт описывать
4369 Порт, используемый процессом epmd. Служба обнаружения конечных точек для узлов RabbitMQ и инструментов CLI.
5671,5672 Используется клиентами AMQP 0-9-1 и AMQP 1.0 с TLS или без него.
15672 Используется клиентом HTTP API, пользовательским интерфейсом администратора и rabbitmqadmin.
Это вступает в силу только в том случае, если плагин управления включен.
25672 Порт, используемый для связи между узлами RabbitMQ или инструментами CLI (порт распределенной службы Erlang).
Ограничение по умолчанию — один порт, обычно порт AMQP + 20000. Этот порт не должен быть открыт снаружи, за исключением случаев, когда это требуется для внешних подключений.
35672~35682 Связь между инструментами CLI и узлами RabbitMQ (порт распределенной службы Erlang).
Как правило, это вышеупомянутый распределенный порт +10000~10010.
61613,61614 Используется клиентами STOMP без TLS и TLS.
Вступает в силу, только если включен плагин STOMP.
1883,8883 Используется клиентами MQTT без TLS и TLS.
Он вступает в силу только в том случае, если включен плагин MQTT.
15674 Используется клиентами STOMP на основе WebSockets.
Вступает в силу, только если включен плагин Web STOMP.
15675 Используется клиентами MQTT на основе WebSockets.
Вступает в силу, только если включен плагин Web MQTT.
15692 Используется для мониторинга метрик Prometheus.
Это вступает в силу только в том случае, если плагин Prometheus включен.

узлы в кластере

что такое репликация

Все данные/состояние, необходимые для работы брокера RabbitMQ, реплицируются на все узлы. Исключением являются очереди сообщений, которые по умолчанию находятся на одном узле, хотя они видны и доступны со всех узлов.
Чтобы реплицировать очереди между узлами в кластере, см. О программе.высокая доступностьдокументация.

Примечание. Это руководство является обязательным условием для зеркального отображения.

Узлы являются равноправными

В некоторых распределенных системах есть узлы-лидеры и ведомые, но обычно это не работает с RabbitMQ. Все узлы в кластере RabbitMQ являются одноранговыми: в ядре RabbitMQ нет специальных узлов. Автономные плагины могут назначать (выбирать) определенные узлы как «особые» узлы на определенный период времени. Например, федеративные ссылки взаимодействуют на определенных узлах кластера. Если этот узел выйдет из строя, соединение будет перезапущено на другом узле.
Инструментальные операции CLI можно выполнять на любом узле. Клиенты HTTP API могут ориентироваться на любой узел кластера.

В RabbitMQ 3.6.7+ подключаемый модуль управления RabbitMQ использует выделенные узлы для сбора и агрегирования данных.

Как инструменты CLI аутентифицируют узлы (и между узлами): файлы cookie Erlang

Файлы cookie используются узлами RabbitMQ и инструментами CLI, такими как rabbitmqctl, для принятия решения о разрешении связи друг с другом. Чтобы два узла могли взаимодействовать друг с другом, они должны иметь один и тот же общий секрет (файл cookie Erlang). Файл cookie — это просто строка из 255 символов. Обычно он хранится в локальном файле. Файл должен быть доступен только владельцу (например, разрешения UNIX 600 или аналогичные). Каждый узел в кластере должен иметь один и тот же файл cookie. Если файл не существует, виртуальная машина Erlang попытается создать файл со случайными числами при запуске сервера RabbitMQ. Использование таких сгенерированных файлов cookie подходит только для сред разработки. Поскольку каждый узел будет генерировать свое собственное значение независимо, эта стратегия на самом деле невозможна в кластерной среде.

Генерация файлов cookie Erlang должна выполняться на этапе развертывания кластера, предпочтительно с использованием инструментов автоматизации и оркестровки, таких как Chef, Puppet, BOSH, Docker или аналогичных.

Путь к файлу cookie

Linux, MacOS, *BSD

В системах UNIX файлы cookie обычно находятся в каталогах /var/lib/rabbitmq/.erlang.cookie (используется сервером RabbitMQ) и $HOME/.erlang.cookie (используется инструментами CLI).

Примечание. Поскольку переменная $HOME (ДОМАШНИЙ каталог пользователя) различается только от человека к человеку, необходимо разместить копию файла cookie для каждого пользователя, который будет использовать инструмент CLI. Это работает для непривилегированных и корневых пользователей.

Windows

В системах Windows расположение файлов cookie зависит от нескольких факторов:
1) Версия Erlang: до или после 20.2;
2) установлены ли одновременно переменные среды HOMEDRIVE и HOMEPATH;

В Erlang 20.2 и более поздних версиях расположение файла cookie выглядит следующим образом:

1)如果HOMEDRIVE和HOMEPATH环境变量都设置:
%HOMEDRIVE%%HOMEPATH%\.erlang.cookie(通常是C:\Users\%USERNAME%\.erlang.cookie,对于%USERNAME%用户)。
2)如果HOMEDRIVE和HOMEPATH环境变量都没设置:
%USERPROFILE%\.erlang.cookie(通常是C:\Users\%USERNAME%\.erlang.cookie);
3)对于RabbitMQ的Windows服务-%USERPROFILE%\.erlang.cookie(通常是C:\WINDOWS\system32\config\systemprofile)。

При использовании службы Windows файл cookie следует скопировать из C:\Windows\system32\config\systemprofile.erlang.cookie в ожидаемое место для пользователя, выполняющего такие команды, как rabbitmqtl.bat.

Erlang 19.3–20.2 расположение файла cookie:

1)如果HOMEDRIVE和HOMEPATH环境变量都设置:
%HOMEDRIVE%%HOMEPATH%\.erlang.cookie(通常是C:\Users\%USERNAME%\.erlang.cookie,对于%USERNAME%用户)。
2)如果HOMEDRIVE和HOMEPATH环境变量都设置:
%USERPROFILE%\.erlang.cookie(通常是C:\Users\%USERNAME%\.erlang.cookie);
3)对于RabbitMQ的Windows服务-%WINDIR%\.erlang.cookie(通常是C:\Windows\.erlang.cookie)。

При использовании службы Windows файлы cookie должны быть скопированы из C:\Windows.erlang.cookie в предполагаемое расположение пользователя, выполняющего такие команды, как rabbitmqtl.bat.

Исправление проблем

Когда узел запускается, он записывает местоположение своего базового каталога. Если ни один из каталогов сервера не будет перезаписан, служба RabbitMQ создаст в нем файлы cookie.

параметры времени выполнения

В качестве альтернативы вы можете добавить параметр «-setcookie value» в переменную среды RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS: RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="-setcookie cookie-value".
Это наименее безопасный вариант и обычно не рекомендуется.

Ошибка аутентификации

RabbitMQ будет регистрировать ошибки (например: «Попытка подключения с запрещенного узла» и «Не удалось выполнить автоматическую кластеризацию»), если файлы cookie настроены неправильно (например, не идентичны).
Если инструмент CLI (такой как rabbitmqctl) не проходит аутентификацию, сообщение обычно выглядит так:

* epmd reports node 'rabbit' running on port 25672
* TCP connection succeeded but Erlang distribution failed
* suggestion: hostname mismatch?
* suggestion: is the cookie set correctly?
* suggestion: is the Erlang distribution using TLS?

Неуместные файлы cookie или несоответствующие значения файлов cookie являются наиболее распространенными сценариями таких сбоев.

При использовании последней версии Erlang/OTP ошибки аутентификации более информативны, а несоответствия файлов cookie могут быть лучше идентифицированы:

* connected to epmd (port 4369) on warp10
* epmd reports node 'rabbit' running on port 25672
* TCP connection succeeded but Erlang distribution failed

* Authentication failed (rejected by the remote node), please check the Erlang cookie

Дополнительную информацию см. в Руководстве по инструментам CLI.

Подсчет узлов и кворум

Поскольку некоторые функции (например, очереди кворума, отслеживание клиентов в MQTT) требуют консенсуса между участниками кластера, настоятельно рекомендуется использовать нечетное количество узлов кластера: 1, 3, 5, 7 и т. д.
Настоятельно не рекомендуется использовать кластер из двух узлов, так как узлы кластера не могут идентифицировать большинство узлов и сформировать соглашение в случае потери связи. Например, когда два узла теряют соединение, клиентские соединения MQTT не будут приниматься, очереди кворума потеряют доступность и т. д. С согласованной точки зрения кластеры из 4 или 6 узлов будут иметь те же характеристики доступности, что и кластеры из 3 и 5 узлов.
Quorum queueРуководство раскрывает эту тему более подробно.

Кластер и клиент

Клиенты могут подключаться к любому узлу и выполнять любые операции при условии, что все члены кластера доступны. Узел будет прозрачно направлять операции на главный узел клиентской очереди.
Для всех поддерживаемых протоколов обмена сообщениями клиенты одновременно подключаются только к одному узлу. В случае сбоя узла клиенты должны иметь возможность повторно подключиться к другому узлу, восстановить свою топологию и продолжить работу. Поэтому большинство клиентов получают список конечных точек (имен хостов или IP-адресов) в качестве параметров подключения. Если поддерживается клиентом, список хостов будет использоваться во время первоначального подключения и восстановления подключения.
В некоторых случаях клиенты могут не иметь возможности прозрачно продолжить работу после подключения к другим узлам. Обычно они включают незеркальные очереди, размещенные на отказавших узлах.

Кластеризация и наблюдаемость

Клиентские соединения, каналы и очереди будут распределены по узлам кластера. Операторы должны иметь возможность проверять и контролировать эти ресурсы на всех узлах кластера.
Инструменты командной строки RabbitMQ, такие как RabbitMQ-diagnostics и rabbitmqctl, предоставляют команды для проверки состояния ресурсов и всего кластера. Некоторые команды фокусируются на состоянии отдельных узлов (например, среда rabbitmq-diagnostics и состояние rabbitmq-diagnostics), другие проверяют состояние всего кластера. Некоторые примеры последних включают rabbitmqctl list_connections, rabbitmqctl list_mqtt_connections, rabbitmqctl list_stomp_connections, rabbitmqctl list_users, rabbitmqctl list_vhosts и т. д.
Такие «кластерные» команды обычно сначала связываются с узлом, обнаруживают элементы кластера и связываются со всеми членами, чтобы получить и объединить их соответствующие состояния. Например, rabbitmqctl list_connections свяжется со всеми узлами, получит их соединения AMQP 0-9-1 и AMQP 1.0 и отобразит их все пользователю. Пользователю не нужно связываться со всеми узлами вручную. Предполагая, что кластер находится в неизменном состоянии (например, ни одно соединение не закрыто и не открыто), две команды CLI, выполняемые последовательно для двух разных узлов, дадут одинаковые или семантически идентичные результаты. Однако команда «Node-local» не дает такого же результата, поскольку два узла редко имеют одинаковое состояние: по крайней мере, их имена узлов разные!
Пользовательский интерфейс управления работает аналогично: узлы, отвечающие на запросы HTTP API, будут распространяться среди других членов кластера и объединять их ответы. В кластере из нескольких узлов с включенным подключаемым модулем управления операторы могут использовать любой узел для доступа к пользовательскому интерфейсу управления. То же самое касается инструментов мониторинга, которые используют HTTP API для сбора данных о состоянии кластера. Нет необходимости делать запросы к каждому узлу кластера по очереди.

Обработка отказа узла

Брокеры RabbitMQ могут допустить отказ одного узла. Узлы можно запускать и останавливать по желанию, если они могут связаться с известным узлом-членом кластера после завершения работы.
Зеркальное отображение очереди позволяет реплицировать содержимое очереди на несколько узлов кластера; незеркальные очереди также могут использоваться в кластере. Поведение незеркальной очереди при сбое узла зависит от долговечности очереди.
Кластеры RabbitMQ имеют несколько режимов работы с сетевыми разделами, в основном ориентированных на согласованность. Кластеризация означает, что его можно использовать в локальных сетях. Запуск кластера через глобальную сеть не рекомендуется. Плагин Shovel или Shovel — лучшее решение для подключения прокси через глобальную сеть.

Обратите внимание, что плагины Shovel и Federation не эквивалентны кластерам.

Метрики и статистика

Каждый узел хранит и агрегирует свои собственные показатели и статистику и предоставляет API для доступа к нему других узлов. Некоторые статистические данные относятся ко всему кластеру, другие — к отдельному узлу. Узел, отвечающий на запрос HTTP API, связывается со своими одноранговыми узлами, чтобы получить их данные, а затем выдает агрегированные результаты.

В RabbitMQ 3.6.7+ подключаемый модуль управления RabbitMQ использует выделенные узлы для сбора и агрегирования данных.

Дисковые и оперативные узлы

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

Примечание. Диск и диск взаимозаменяемы. В подавляющем большинстве случаев вы хотите, чтобы все узлы были дисковыми узлами; узлы оперативной памяти — это особый случай, который можно использовать для повышения производительности кластеров с высокопроизводительными очередями, обменами или привязками. Узлы RAM не обеспечивают более высокую скорость передачи сообщений. В случае сомнений используйте только дисковые узлы.

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

Кластерная конструкция

Ниже приведен текст для ручной настройки и управления кластером RabbitMQ на трех машинах (rabbit1, rabbit2, rabbit3). Рекомендуется изучить этот пример, прежде чем использовать варианты формирования кластера, более подходящие для автоматизации. Мы предполагаем, что пользователь вошел на все три машины, что RabbitMQ уже установлен на машинах и что сценарии rabbitmq-server и rabbitmqctl находятся в пути пользователя. Этот скрипт можно изменить для работы на одном хосте, подробнее ниже.

запустить один узел

Кластер настраивается путем перенастройки существующих узлов RabbitMQ в конфигурацию кластера. Итак, первый шаг — запустить RabbitMQ обычным способом на всех узлах:

# on rabbit1
rabbitmq-server -detached
# on rabbit2
rabbitmq-server -detached
# on rabbit3
rabbitmq-server -detached

Это создаст три независимых брокера RabbitMQ, по одному на каждом узле, что подтверждается командой cluster_status:

# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1]}]},{running_nodes,[rabbit@rabbit1]}]
# => ...done.

# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit2]}]},{running_nodes,[rabbit@rabbit2]}]
# => ...done.

# on rabbit3
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit3 ...
# => [{nodes,[{disc,[rabbit@rabbit3]}]},{running_nodes,[rabbit@rabbit3]}]
# => ...done.

Имя узла брокера RabbitMQ, запущенное из сценария оболочки rabbitmq-server, — rabbit@shorthostname, где короткое имя узла — в нижнем регистре (например, rabbit@rabbit1 выше).
В Windows, если используется пакетный файл rabbitmq-server.bat, короткое имя узла пишется с большой буквы (например, rabbit@RABBIT1). При вводе имен узлов важен регистр, и эти строки должны точно совпадать.

Создать кластер

Чтобы соединить три узла в кластере, мы позволяем двум из них (например, rabbit@rabbit2 и rabbit@rabbit3) присоединиться к кластеру третьего узла (например, rabbit@rabbit1). Перед этим два вновь присоединившихся участника должны быть сброшены.
Сначала мы объединяем Rabbit@rabbit2 с Rabbit@rabbit1 в кластер. Для этого мы останавливаем приложение RabbitMQ на rabbit@rabbit2 и присоединяемся к кластеру rabbit@rabbit1, затем перезапускаем приложение RabbitMQ.

Обратите внимание, что узлы должны быть сброшены, чтобы присоединиться к существующему кластеру. Сброс узла приведет к удалению всех ресурсов и данных, которые ранее существовали на этом узле. Это означает, что узел не может одновременно быть членом кластера и сохранять свои существующие данные. При необходимости можно использовать сине-зеленую стратегию развертывания или резервного копирования и восстановления.

# on rabbit2
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit2 ...done.

rabbitmqctl reset
# => Resetting node rabbit@rabbit2 ...

rabbitmqctl join_cluster rabbit@rabbit1
# => Clustering node rabbit@rabbit2 with [rabbit@rabbit1] ...done.

rabbitmqctl start_app
# => Starting node rabbit@rabbit2 ...done.

Мы можем видеть, что два узла подключены к кластеру, выполнив команду cluster_status на одном из узлов:

# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
# =>  {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}]
# => ...done.

# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
# =>  {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}]
# => ...done.

Теперь мы добавим rabbit@rabbit3 в тот же кластер. Шаги, описанные выше, такие же, за исключением того, что на этот раз мы кластеризуем rabbit2, чтобы доказать, что выбор узла в кластере не имеет значения — достаточно предоставить сеть узлов, и будут кластеризованы узлы, к которым принадлежат узлы кластера. назначенный.

# on rabbit3
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit3 ...done.

# on rabbit3
rabbitmqctl reset
# => Resetting node rabbit@rabbit3 ...

rabbitmqctl join_cluster rabbit@rabbit2
# => Clustering node rabbit@rabbit3 with rabbit@rabbit2 ...done.

rabbitmqctl start_app
# => Starting node rabbit@rabbit3 ...done.

Мы можем видеть, что три узла подключены к кластеру, выполнив команду cluster_status на любом узле:

# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# =>  {running_nodes,[rabbit@rabbit3,rabbit@rabbit2,rabbit@rabbit1]}]
# => ...done.

# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# =>  {running_nodes,[rabbit@rabbit3,rabbit@rabbit1,rabbit@rabbit2]}]
# => ...done.

# on rabbit3
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit3 ...
# => [{nodes,[{disc,[rabbit@rabbit3,rabbit@rabbit2,rabbit@rabbit1]}]},
# =>  {running_nodes,[rabbit@rabbit2,rabbit@rabbit1,rabbit@rabbit3]}]
# => ...done.

Выполняя вышеуказанные шаги, мы можем добавлять новые узлы в кластер в любое время, пока кластер работает.

Перезапустите узлы кластера

Узлы, подключенные к кластеру, могут быть остановлены в любой момент. Они также могут дать сбой или быть прекращены операционной системой. Во всех случаях остальная часть кластера может продолжать работать, и при перезапуске других узлов кластера узлы автоматически «синхронизируются» с ними.
Обратите внимание, что некоторые стратегии обработки разделов могут работать иначе и влиять на другие узлы. Мы выключаем узлы rabbit@rabbit1 и rabbit@rabbit3, проверяя состояние кластера на каждом шаге:

# on rabbit1
rabbitmqctl stop
# => Stopping and halting node rabbit@rabbit1 ...done.

# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# =>  {running_nodes,[rabbit@rabbit3,rabbit@rabbit2]}]
# => ...done.

# on rabbit3
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit3 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# =>  {running_nodes,[rabbit@rabbit2,rabbit@rabbit3]}]
# => ...done.

# on rabbit3
rabbitmqctl stop
# => Stopping and halting node rabbit@rabbit3 ...done.

# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# =>  {running_nodes,[rabbit@rabbit2]}]
# => ...done.

Теперь снова запускаем узел, проверяя состояние кластера по мере его запуска:

# on rabbit1
rabbitmq-server -detached
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# =>  {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}]
# => ...done.

# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# =>  {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}]
# => ...done.

# on rabbit3
rabbitmq-server -detached

# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# =>  {running_nodes,[rabbit@rabbit2,rabbit@rabbit1,rabbit@rabbit3]}]
# => ...done.

# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# =>  {running_nodes,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]
# => ...done.

# on rabbit3
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit3 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# =>  {running_nodes,[rabbit@rabbit2,rabbit@rabbit1,rabbit@rabbit3]}]
# => ...done.

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

После остановки узла и перезапуска выберите подключенный к сети член кластера (учитывайте только дисковые узлы) для синхронизации. При перезапуске узла по умолчанию он пытается связаться с этим узлом 10 раз с тайм-аутом ответа 30 секунд. Если одноранговый узел доступен в течение этого интервала, узел успешно запустится, синхронизирует то, что ему нужно, с одноранговым узлом и продолжит работу. Если одноранговый узел недоступен, перезапущенный узел сдастся и остановится автоматически.
Когда узел не имеет подключенных узлов во время отключения, он запустится без попыток синхронизации с какими-либо известными узлами. Однако он не запускается как автономный узел, и узлы смогут к нему присоединиться.
Узел, который повторно подключается после изменения имени узла или имени хоста, может начать работу как пустой узел, если его путь к каталогу данных изменится соответствующим образом. Такие узлы не смогут повторно присоединиться к кластеру. Когда узел отключается, его одноранговые узлы могут быть сброшены или запущены с пустым каталогом данных. В этом случае узел восстановления также не сможет воссоединиться со своими одноранговыми узлами, поскольку внутренние идентификаторы кластера хранилища данных больше не будут совпадать.

Рассмотрим следующие сценарии:
1) Формируется кластер, состоящий из трех узлов A, B и C;
2) узел А отключен;
3) Узел B перезагружается;
4) Запускается узел А;
5) узел A пытается воссоединиться с B, но идентификатор кластера B изменился;
6) узел B не распознает A как известного члена кластера, поскольку он сброшен;

В этом случае Node B отклонит попытку кластеризации A с соответствующим сообщением об ошибке в журнале: Узел Rabbit@node1.local считает, что он кластеризован с узлом Rabbit@node2.local, но Rabbit@node2.local не согласен. В этом случае B может быть сброшен снова, и A сможет подключиться, или A может быть сброшен, и B будет успешно подключен. Таким образом, когда весь кластер отключается, последний отключаемый узел является единственным узлом, на котором в момент отключения ничего не работает. Узел может запускаться без предварительной связи с каким-либо узлом. Поскольку узлы будут пытаться соединиться с известными одноранговыми узлами до 5 минут (по умолчанию), в течение этого времени узлы можно перезапускать в любом порядке. В этом случае они будут успешно переподключаться один за другим. Это временное окно можно настроить с помощью двух параметров конфигурации:

# wait for 60 seconds instead of 30
mnesia_table_loading_retry_timeout = 60000

# retry 15 times instead of 10
mnesia_table_loading_retry_limit = 15

Настраивая эти параметры и настраивая временной интервал, в течение которого известные конечные точки должны вернуться, можно учитывать сценарии повторного развертывания в масштабе всего кластера, выполнение которых может занять более 5 минут.
Во время обновления иногда последний остановленный узел должен быть первым узлом, который будет запущен после обновления. Узел будет назначен для выполнения миграции схемы в масштабе всего кластера, с которой другие узлы смогут синхронизироваться и применяться при повторном присоединении. В некоторых случаях последний узел, перешедший в автономный режим, не может быть восстановлен. Его можно удалить из кластера с помощью команды get_cluster_node rabbitmqctl. Кроме того, на узле можно использовать команду force_boot rabbitmqctl для начальной загрузки узлов без попытки синхронизации с какими-либо узлами (как если бы они были в последний раз, когда они были недоступны). Обычно это необходимо только в том случае, если последний узел, который отключился, или группа узлов никогда не вернется в оперативный режим.

расщеплять

Бывают случаи, когда узел необходимо удалить из кластера. Оператор должен сделать это явным образом с помощью команды rabbitmqctl. Некоторые механизмы однорангового обнаружения поддерживают проверки работоспособности узлов и принудительное удаление узлов, неизвестных серверной части обнаружения. Эта функция не является обязательной (по умолчанию отключена). Сначала мы удаляем rabbit@rabbit3 из кластера, возвращая его к автономной работе. Для этого мы останавливаем приложение RabbitMQ на rabbit@rabbit3, перезагружаем узел и перезапускаем приложение RabbitMQ.

# on rabbit3
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit3 ...done.

rabbitmqctl reset
# => Resetting node rabbit@rabbit3 ...done.
rabbitmqctl start_app
# => Starting node rabbit@rabbit3 ...done.

Обратите внимание, что использование rabbit@rabbit3 в качестве узла также работает.

Запустите команду cluster_status на узле, чтобы убедиться, что rabbit@rabbit3 больше не является частью кластера и работает независимо:

# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
# => {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}]
# => ...done.

# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
# =>  {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}]
# => ...done.

# on rabbit3
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit3 ...
# => [{nodes,[{disc,[rabbit@rabbit3]}]},{running_nodes,[rabbit@rabbit3]}]
# => ...done.

Мы также можем удаленно удалять узлы, что очень удобно. Например, когда приходится иметь дело с не отвечающим узлом. Например, мы можем удалить rabbit@rabbi1 из rabbit@rabbit2.

# on rabbit1
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit1 ...done.

# on rabbit2
rabbitmqctl forget_cluster_node rabbit@rabbit1
# => Removing node rabbit@rabbit1 from cluster ...
# => ...done.

Обратите внимание, что Rabbit1 по-прежнему считает, что он кластеризован с Rabbit2, и попытка его запуска приведет к ошибке. Нам нужно будет сбросить его, чтобы иметь возможность запустить его снова.

# on rabbit1
rabbitmqctl start_app
# => Starting node rabbit@rabbit1 ...
# => Error: inconsistent_cluster: Node rabbit@rabbit1 thinks it's clustered with node rabbit@rabbit2, but rabbit@rabbit2 disagrees

rabbitmqctl reset
# => Resetting node rabbit@rabbit1 ...done.

rabbitmqctl start_app
# => Starting node rabbit@rabbit1 ...
# => ...done.

Команда cluster_status теперь показывает, что все три узла работают как автономные брокеры RabbitMQ:

# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1]}]},{running_nodes,[rabbit@rabbit1]}]
# => ...done.

# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit2]}]},{running_nodes,[rabbit@rabbit2]}]
# => ...done.

# on rabbit3
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit3 ...
# => [{nodes,[{disc,[rabbit@rabbit3]}]},{running_nodes,[rabbit@rabbit3]}]
# => ...done.

Обратите внимание, что Rabbit@rabbit2 сохраняет оставшееся состояние кластера, а Rabbit@rabbit1 и Rabbit@rabbit3 являются недавно инициализированными брокерами RabbitMQ. Если мы хотим повторно инициализировать rabbit@rabbit2, мы выполняем те же шаги, что и для других узлов:

# on rabbit2
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit2 ...done.
rabbitmqctl reset
# => Resetting node rabbit@rabbit2 ...done.
rabbitmqctl start_app
# => Starting node rabbit@rabbit2 ...done.

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

Как сбросить узел

Иногда может потребоваться перезагрузить узел (удалить все его данные), а затем снова подключить его к кластеру. Вообще говоря, возможны два случая: когда узел работает, и когда узел не может запуститься или не может отвечать на команды инструмента CLI, например, из-за проблемы (такой как ERL-430).
При сбросе узла будут удалены все его данные, информация о членстве в кластере, настроенные параметры среды выполнения, пользователи, виртуальные хосты и любые другие данные узла. Это также навсегда удалит узел из его кластера.
Чтобы сбросить работающий и отвечающий узел, сначала остановите RabbitMQ на нем с помощью rabbitmqctl stop_app, а затем сбросьте его с помощью Rabbitmqctl reset:

# on rabbit1
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit1 ...done.
rabbitmqctl reset
# => Resetting node rabbit@rabbit1 ...done.

Узел, который не отвечает, должен быть сначала остановлен любыми необходимыми средствами. Это уже относится к узлам, которые не запускаются. Затем перезапишите расположение каталога данных узла или [пере]переместите существующее хранилище данных. Это сделает узел пустым. Если есть, ему необходимо дать указание присоединиться к исходному кластеру. Узел, который перезагружается и снова присоединяется к исходному кластеру, синхронизирует все виртуальные хосты, пользователей, разрешения и топологию (очереди, обмены, привязки), параметры времени выполнения и политики. Он может синхронизировать содержимое зеркальной очереди, если он выбран для размещения реплики. Неотзеркаленное содержимое очереди на узлах сброса будет потеряно. Восстановление каталога данных очереди на узле сброса, который синхронизировал свою схему с одноранговым узлом, не гарантирует, что данные будут доступны для клиентов, поскольку исходное расположение затронутой очереди могло измениться.

Обновите кластер

Вы можете найти в руководстве по обновлениюОбновите кластеринструкция о.

Кластер из отдельных машин

В некоторых случаях может быть полезно запустить кластер узлов RabbitMQ на одной машине. Это часто полезно для экспериментов по кластеризации на настольном или портативном компьютере без необходимости развертывания нескольких виртуальных машин для кластера. Чтобы запустить несколько узлов RabbitMQ на одном компьютере, необходимо убедиться, что узлы имеют разные имена узлов, места хранения данных, расположение файла журнала и привязку к разным портам, включая те, которые используются подключаемыми модулями. См. RABBITMQ_NODENAME, RABBITMQ_NODE_PORT и RABBITMQ_DIST_PORT в руководстве по настройке, а также RABBITMQ_MNESIA_DIR, RABBITMQ_CONFIG_FILE и RABBITMQ_LOG_BASE в руководстве по размещению файлов и каталогов.
Несколько узлов на одном хосте можно запустить вручную, многократно вызывая rabbitmq-server (rabbitmq-server.bat в Windows). Например:

RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit rabbitmq-server -detached
RABBITMQ_NODE_PORT=5673 RABBITMQ_NODENAME=hare rabbitmq-server -detached
rabbitmqctl -n hare stop_app
rabbitmqctl -n hare join_cluster rabbit@`hostname -s`
rabbitmqctl -n hare start_app

Будет создан двухузловой кластер, причем оба узла будут действовать как дисковые узлы. Обратите внимание, что если узел прослушивает какие-либо порты, кроме AMQP 0-9-1 и AMQP 1.0, их необходимо настроить во избежание конфликтов. Это можно сделать через командную строку:

RABBITMQ_NODE_PORT=5672 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15672}]" RABBITMQ_NODENAME=rabbit rabbitmq-server -detached
RABBITMQ_NODE_PORT=5673 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15673}]" RABBITMQ_NODENAME=hare rabbitmq-server -detached

Оба узла будут запущены при установке подключаемого модуля управления (который затем можно будет кластеризовать).

изменение имени хоста

Узлы RabbitMQ взаимодействуют друг с другом, используя имена хостов. Поэтому все имена узлов должны разрешать имена всех узлов кластера. Это также относится к таким инструментам, как rabbitmqctl.
Кроме того, по умолчанию RabbitMQ использует текущее имя хоста системы для обозначения каталога базы данных. При изменении имени хоста создается новая пустая база данных. Чтобы избежать потери данных, очень важно установить фиксированное разрешаемое имя хоста.
Узлы RabbitMQ необходимо перезапускать при каждом изменении имени хоста.
Аналогичного эффекта можно добиться, используя rabbit@localhost в качестве имени прокси-узла. Результатом этого решения является то, что кластер не будет работать, поскольку выбранное имя хоста не может быть преобразовано в маршрутизируемый адрес с удаленного хоста. То же самое происходит при вызове команды rabbitmqctl с удаленного хоста. Более сложное решение, не подверженное этой уязвимости, — использовать DNS, например Amazon Route 53, работающий на EC2. Если вы хотите использовать полное имя хоста для имени узла (по умолчанию RabbitMQ использует короткое имя), и это полное имя хоста может быть разрешено с помощью DNS, вы можете захотеть установить переменную среды RABBITMQ_USE_LONGNAME=true.
Дополнительную информацию см. в разделе о разрешении имен хостов.

Узел брандмауэра

Узлы кластера брандмауэра возникают, когда узлы расположены в центре обработки данных или надежной сети, но разделены брандмауэром. Точно так же кластеризация не рекомендуется в глобальной сети или при ненадежном сетевом канале между узлами.
Узнайте больше в разделе «Порты» выше и в специальном руководстве по работе с сетью RabbitMQ.

Версии Erlang в разных кластерах

На всех узлах в кластере должна быть запущена одна и та же дополнительная версия Erlang: 21.3.4 и 21.3.6 можно смешивать, но 21.0.1 и 21.3.6 (или 20.3 и 22.0.6) смешивать нельзя. Совместимость между различными версиями исправлений Erlang/OTP может различаться, но это бывает редко.

Подключиться к кластеру с клиента

Клиенты могут нормально подключаться к любому узлу в кластере. Если этот узел выйдет из строя, а остальная часть кластера уцелеет, клиенты должны заметить закрытые соединения и иметь возможность повторно подключиться к некоторым из уцелевших членов кластера. Как правило, не рекомендуется помещать имена узлов или IP-адреса узлов в клиентское приложение: это вносит негибкость и требует правок в клиентском приложении при изменении конфигурации кластера или изменении количества узлов в кластере, повторной компиляции и повторного развертывания. Вместо этого мы рекомендуем более абстрактный подход: это может быть служба динамического DNS с очень короткой конфигурацией TTL, или простой балансировщик нагрузки TCP, или какой-то мобильный IP, реализованный с использованием кардиостимулятора или аналогичной технологии. В общем, этот аспект управления подключениями к узлам в кластере выходит за рамки самого RabbitMQ, и мы рекомендуем использовать другие методы, разработанные специально для решения этих проблем.

Кластер с узлами оперативной памяти

Узлы RAM хранят только свои метаданные в памяти. Поскольку узлам RAM не нужно записывать на диск так часто, как узлам диска, они могут работать лучше. Однако обратите внимание, что, поскольку данные постоянной очереди всегда хранятся на диске, повышение производительности влияет только на управление ресурсами (например, добавление/удаление очередей, обменов или виртуальных хостов), а не на скорость публикации или потребления.
RAM-узлы — это продвинутый случай, их не следует использовать при настройке вашего первого кластера. У вас должно быть достаточно дисковых узлов для удовлетворения потребностей в избыточности, а затем при необходимости добавьте дополнительные узлы оперативной памяти. Кластер только с узлами RAM слишком нестабилен, если кластер остановится, вы не сможете запустить его снова и потеряете все данные. RabbitMQ предотвратит кластер, который во многих случаях создает только узлы оперативной памяти, но не может предотвратить его полностью.
Для простоты в приведенном здесь примере показан кластер с одним диском и одним узлом оперативной памяти; такой кластер является плохим выбором для проектирования.

Создайте узел оперативной памяти

Мы можем объявить узел как узел оперативной памяти, когда он впервые присоединяется к кластеру. Мы делаем это с помощью rabbitmqctl join_cluster, как и раньше, но передаем параметр «--ram»:

# on rabbit2
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit2 ...done.

rabbitmqctl join_cluster --ram rabbit@rabbit1
# => Clustering node rabbit@rabbit2 with [rabbit@rabbit1] ...done.

rabbitmqctl start_app
# => Starting node rabbit@rabbit2 ...done.

В состоянии кластера узел RAM показан на следующем рисунке:

# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1]},{ram,[rabbit@rabbit2]}]},
# =>  {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}]
# => ...done.

# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1]},{ram,[rabbit@rabbit2]}]},
# =>  {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}]
# => ...done.

Изменить тип узла

Мы можем изменить тип узла с RAM на Disk и наоборот. Предположим, мы хотим поменять местами типы rabbit@rabbit2 и rabbit@rabbit1, преобразовав первый из узла RAM в узел диска, а второй — из узла диска в узел RAM. Для этого мы можем использовать команду change_cluster_node_type. Узел должен быть остановлен в первую очередь.

# on rabbit2
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit2 ...done.

rabbitmqctl change_cluster_node_type disc
# => Turning rabbit@rabbit2 into a disc node ...done.

rabbitmqctl start_app
# => Starting node rabbit@rabbit2 ...done.

# on rabbit1
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit1 ...done.

rabbitmqctl change_cluster_node_type ram
# => Turning rabbit@rabbit1 into a ram node ...done.

rabbitmqctl start_app
# => Starting node rabbit@rabbit1 ...done.

Предыдущий:Серия RabbitMQ (9) Обзор распределенного RabbitMQ
Следующий:Серия RabbitMQ (11) Распределенный RabbitMQ (зеркалирование очереди)