RabbitMQ — приложение, предоставляющее шину для обмена сообщениями по протоколу AMQP. Написан «кролик» на Erlang , а следовательно автоматически масштабируем, отказоустойчив и учится за две недели 🙂 RabbitMQ используют в таких компаниях, как VMware, Mozilla и AT&T, а также в бесчисленном количестве веб-сайтов. В этой заметке мы попытаемся разобраться, для каких практических задач нужен RabbitMQ, а также как поднять RabbitMQ-кластер в облаке Amazon’а .
Зачем нужен RabbitMQ
До тех пор, пока ваше приложение работает по принципу «получили запрос, сходили в разные базы, составили ответ, передали его пользователю и забыли», никакие RabbitMQ, скорее всего, вам не нужны. Все становится куда интереснее, как только у вас возникает необходимость посылать какие-то данные просто так, без запроса. Например, если при получении нового личного сообщения пользователю нужно сообщить об этом по WebSockets, чтобы в UI обновлялся бейджик с количеством новых сообщений. Мобильным приложениям имеет смысл отдавать без запроса вообще все и всегда. Если приложение имеет все, что может захотеть увидеть пользователь, оно не тупит при медленном интернете и может работать даже оффлайн.
Таким образом, действия одного пользователя могут привести к посылке каких-то данных другому пользователю. Проблема в том, что разные пользователи могут находиться на разных бэкендах. Значит, бэкенды должны как-то между собой общаться. Можно просто периодически выполнять запросы к БД, но это медленно и ресурсоемко. Еще можно воспользоваться распределенными акторами в Erlang или Akka Cluster , но у этого подхода есть целый ряд своих проблем . Неплохим и, по-видимому, популярным, решением является использование RabbitMQ.
Среди других юзкейсов можно отметить:
- Раздача воркерам каких-то фоновых задач. Отправка писем и SMS, импорт каких-то больших объемов данных, и прочее в таком духе;
- Подписка на какие-то часто запрашиваемые данные. Например, все бэкенды могут держать в памяти текущие котировки по всем инструментам в системе, и получать обновления котировок по AMQP;
- Публикация и последующая агрегация каких-то событий в системе. Например, мы пишем в RabbitMQ события «пользователь вошел» и «пользователь вышел», а другой сервис по этим событиям агрегирует длины сессий и рисует красивые графики;
Еще авторы книжек в качестве хороших примеров использования RabbitMQ любят называть RPC. А также, что можно реализовывать новый функционал путем написания небольших приложений, которые подписываются только на интересующие их события. На данный момент мой опыт не позволяет ни подтвердить, ни опровергнуть, является ли хорошей идеей использование RabbitMQ для таких сценариев.
Немного теории
В протоколе AMQP используются такие понятия, как Exchange, Route и Queue. Лучше всего их объясняет следующая картинка:
Пояснение:
- Exchange — место, куда пишутся сообщения;
- Route — штука, которая связывает Exchange и Queue;
- Queue — место, откуда читаются сообщения;
Такая странная на первый взгляд схема позволяет добиться большой гибкости. Приложение может публиковать сообщения в N эксченджей, читать из M очередей и эти очереди и эксченджи могут быть связаны между собой как угодно. То есть, одна очередь может забирать сообщения из нескольких эксченджей. Попробуйте добиться такой же гибкости, используя одни только очереди. При этом из одной очереди могут читать сразу несколько клиентов, в этом случае сообщения раздаются им по round-robin’у. В рамках одного TCP-соединения клиент может создавать несколько каналов, каждый из которых подписан на свои очереди. Более того, эксченджи бывают нескольких видов — Direct, Fanout, Topic, Headers, очереди могут быть persistent и in-memory, сообщения могут иметь TTL, есть такое понятие как VHost (своего рода аналог разбиения на отдельные базы данных в СУБД), уведомления о доставке сообщений (Ack), и плюс ко всему этому поддерживаются плагины.
На данном этапе нам должно хватить этих сведений. Подробности, как обычно, можно будет найти по ссылкам в конце поста.
Установка RabbitMQ
Качаем deb-пакет с последней версией RabbitMQ отсюда . Также нам понадобится сборка Erlang от Erlang Solutions, которую можно сказать здесь . Я использовал свежайший Erlang версии 18.0, но вам может захотеться использовать более стабильный и проверенный временем 17.5, или даже R16B03, что на данный момент идет в Ubuntu по умолчанию.
sudo dpkg -i rabbitmq-server_3.5.3- 1 _all.deb
sudo apt-get install -f
Если все было сделано правильно, команда:
… покажет развесистый эрланговый proplist с состоянием приложения.
Включим красивенькую веб-мородочку:
Создаем нового пользователя-администратора:
sudo rabbitmqctl set_user_tags afiskon administrator
Заходим на rabbitmq.example.ru:15672, там в первую очередь идем во вкладку Admin и удаляем пользователя guest. Веб-интерфейс RabbitMQ по крутости не уступает веб-интерфейсу Couchbase . Здесь можно смотреть все очереди, все эксченджи, есть красивые графики, вот это все. В качестве домашнего задания можете создать нового пользователя, новый vhost, а затем дать доступ к этому vhost новому пользователю, а также администратору. Без последнего шага вы не сможете смотреть очереди и эксченджи в vhost’е через веб-интерфейс.
Настройка кластера
На второй машине устанавливаем RabbitMQ точно так же. Затем вместо того, чтобы создать нового пользователя, делаем следующее.
Прописываем в /var/lib/rabbitmq/.erlang.cookie значение с первой машины. Куки должны быть одинаковыми с точностью до наличия перевода строки в файле. Поскольку на первой машине его нет, проще всего написать как-то так:
Далее:
sudo rabbitmqctl reset
sudo rabbitmqctl join_cluster rabbit @ ip- 172 — 12 — 23 — 34
sudo rabbitmqctl start_app
Важно указывать именно доменное имя (ip-172-12-23-34), а не IP, иначе ноды не смогут соединиться.
Проверяем:
Должны увидеть что-то вроде:
[{nodes,[{disc,[‘rabbit@ip-172-12-23-34′,’rabbit@ip-172-45-56-67’]}]},
{running_nodes,[‘rabbit@ip-172-12-23-34′,’rabbit@ip-172-45-56-67’]},
{cluster_name,<<«rabbit@ip-172-12-23-34.us-west-1.foo.bar»>>},
{partitions,[]}]
В случае проблем изучайте логи в каталоге /var/log/rabbitmq/. Например, частая проблема в мире Erlang — залипший демон epmd. Если команда:
… говорит, что имя rabbitmq зарегистрировано, а вы точно знаете, что сервис сейчас остановлен, epmd нужно прибить:
Также не поленитесь проверить, как ведет себя RabbitMQ при падении одной из машин, а также при нетсплитах. Поведение RabbitMQ при использовании в режиме in-memory pubsub оказалось следующим:
- При остановке одной машины и последующем ее запуске все работает в соответствии с ожиданиями. Машина сама присоединяется обратно к кластеру и все хорошо;
- При нетсплите сообщения, публикуемые на разных нодах, перестают приходить клиентам на других нодах. После починки нетсплита делаем restart сервиса на отвалившейся ноде и все снова круто работает;
Однако ваша версия RabbitMQ при вашем способе использования может вести себя иначе. Лучше проверить поведение «кролика» в граничных случаях заранее, чем потом разбираться уже в боевом окружении.
Заключение
Как всегда, настройка фаервола , OpenVPN , прикручивание TLS к веб-админке и прочее выходит за рамки данного поста. Кроме того, написание приложения, использующего RabbitMQ, также будет рассмотрено в одной из будущих заметок, так что следите за обновлениями блога.
Ссылки по теме:
А используете ли вы RabbitMQ и если да, то для каких задач?
Дополнение: См также Пример обмена сообщениями через RabbitMQ на Scala и Тонкости использования NOTIFY/LISTEN в PostgreSQL .