КРАТКОЕ ВВЕДЕНИЕ В RABBITMQ

RabbitMQ — это пол­но­цен­ная и щед­ро удоб­рен­ная фича­ми оче­редь сооб­ще­ний. В отли­чие от ZeroMQ, кото­рый встра­и­ва­ет­ся в при­ло­же­ния, RabbitMQ — сер­вис-посред­ник. Он раз­гра­ни­чи­ва­ет пра­ва досту­па, под­дер­жи­ва­ет шиф­ро­ва­ние, сохра­не­ние сооб­ще­ний на диск (что­бы пере­жить пла­но­вое отклю­че­ние элек­три­че­ства), рабо­ту в кла­сте­рах и даже дуб­ли­ро­ва­ние сер­ви­сов для повы­шен­ной живу­че­сти. К тому же он напи­сан на Erlang, за что авто­ма­ти­че­ски ста­но­вит­ся неуби­ва­е­мым и под­дер­жи­ва­е­мым на боль­шин­стве попу­ляр­ных ОС.

В этом посте мы посмот­рим, насколь­ко тяже­ло отправ­лять и полу­чать сооб­ще­ния с RabbitMQ, да и вооб­ще, на что он похож вбли­зи. В каче­стве плат­фор­мы будет Убун­та (запер­тая внут­ри Docker кон­тей­не­ра), но сго­дил­ся бы и Mac, и Windows.

Установка

Я не буду фоку­си­ро­вать­ся на уста­нов­ке слиш­ком силь­но, тем более что офи­циль­ная доку­мен­та­ция весь­ма ниче­го, но самый про­стой спо­соб полу­чить рабо­та­ю­щий RabbitMQ на сво­ей машине — через Docker и rabbitmq образ.

Внут­ри полу­чив­ше­го­ся кон­тей­не­ра будет пол­но­стью рабо­чий сер­вер сооб­ще­ний, кото­рый нуж­но про­сто запустить:

Если с Доке­ром у вас не сло­жи­лось, то на насто­я­щей машине уста­но­вить RabbitMQ тоже не про­бле­ма. Вот на что это похо­же на Убунте:

На дру­гих систе­мах, воз­мож­но, будет даже лег­че, но я всё рав­но очень реко­мен­дую потра­тить немно­го вре­ме­ни и разо­брать­ся с Docker, пото­му что он иде­аль­но под­хо­дит для изу­че­ния таких вот вещей. Про­сто берем образ с уже уста­нов­лен­ным RabbitMQRedisElasticsearchJenkins — да вооб­ще с чем угод­но, игра­ем­ся вво­лю, а потом уда­ля­ем кон­тей­нер целиком.

Отправка и получение сообщений с rabbitmqadmin

Отпра­вить и полу­чить сооб­ще­ние мож­но не напи­сав ни строч­ки кода. В ком­плект с RabbitMQ идёт management plugin, в кото­ром есть милая python-ути­лит­ка —  rabbitmqadmin . С ней мож­но созда­вать и уда­лять оче­ре­ди сооб­ще­ний, про­ве­рять их ста­тус, а так же отправ­лять, соб­ствен­но, сами сооб­ще­ния. Management plugin выклю­чен по умол­ча­нию, так что его при­дёт­ся сна­ча­ла вклю­чить. Прав­да, мож­но было бы про­сто взять rabbitmq:management образ, где это уже сде­ла­но, но мы бы сэко­но­ми­ли все­го одну команду.

Включаем rabbitmqadmin

..и всё.  rabbitmqadmin рас­по­ло­жен в уди­ви­тель­ной глу­би­ны и непро­из­но­си­мо­сти пап­ке, так что его сра­зу сто­ит доба­вить либо в PATH пере­мен­ную, либо сра­зу в /usr/local/bin/.

Теперь попро­бу­ем что-нибудь создать.

Играем с очередями и сообщениями

RabbitMQ — сер­вис-посред­ник, кото­рый созда­ёт и управ­ля­ет оче­ре­дя­ми сооб­ще­ний. В архи­тек­ту­ре рас­пре­де­лен­но­го при­ло­же­ния он будет где-то посередине:

Во-пер­вых, давай­те посмот­рим, какие оче­ре­ди в нём есть по-умолчанию:

Ника­ких. Но это вполне себе поправимо:

Теперь у нас есть новая оче­редь с назва­ни­ем «demoqueue». Так как она дер­жит сооб­ще­ния в памя­ти ( durable=false ), то паде­ние сер­ви­са похо­ро­нит под собой все сооб­ще­ния, кото­рые он не успел отпра­вить. Если это про­бле­ма, то мож­но сде­лать durable=true .

Кста­ти о сооб­ще­ни­ях — отпра­вим-ка чего:

Коман­да отправ­ки сооб­ще­ния не содер­жит ни одно­го сло­ва, хоть отда­лён­но напо­ми­на­ю­ще­го «отправ­ка» и «сооб­ще­ние», так что сто­ит чуть-чуть углу­бить­ся в терминологию.

Кто такой AMQP

В мире оче­ре­дей сооб­ще­ний есть попыт­ка прий­ти к обще­му стан­дар­ту, и эта попыт­ка сего­дня назы­ва­ет­ся AMQP (Advanced Message Queuing Protocol). По это­му стан­дар­ту меж­ду отпра­ви­те­лем сооб­ще­ния и оче­ре­дью дол­жен быть еще один игрок — exchange. Отпра­ви­тель кла­дёт сооб­ще­ние в exchange, а уже exchange в зави­си­мо­сти от сво­е­го типа и того, какие оче­ре­ди к нему при­вя­за­ны, реша­ет, куда оно отправится.

Напри­мер, в AMQP есть exchange типа «fanout», кото­рый отправ­ля­ет копии сооб­ще­ния сра­зу всем «сво­им» очередям.

Дру­гой тип exchange — «direct» — отправ­ля­ет сооб­ще­ние толь­ко одной оче­ре­ди. Какой имен­но — опре­де­ля­ет марш­рут­ный ключ (routing key), подо­рож­ни­ком при­кла­ды­ва­е­мый к сообщению.

Нако­нец, что­бы пол­но­стью соот­вет­ство­вать про­то­ко­лу AMQP,  у бро­ке­ра долж­ны быть несколь­ко exchange по-умол­ча­нию. Один из них — amq.default — дол­жен иметь тип ‘direct’. Как толь­ко созда­ёт­ся новая оче­редь, она авто­ма­ти­че­ски к нему при­вя­зы­ва­ет­ся, и routing key будет сов­па­дать с её именем.

Так как RabbitMQ под­дер­жи­ва­ет AMQP пол­но­стью, то в нём есть и exchange, и amq.default, так что отправ­ка сооб­ще­ния такой стран­ной коман­дой теперь выгля­дит чуть-чуть более логичной:

Нако­нец, опуб­ли­ко­ван­ное сооб­ще­ние мож­но про­чи­тать назад:

Чита­ют сооб­ще­ние напря­мую из оче­ре­ди, без exchange. Я доба­вил в конец пара­метр  requeue=true , что­бы после того, как сооб­ще­ние про­чи­та­ли, оно вер­ну­лось в очередь.

Отправка и получение сообщений через NodeJS

Отправ­лять и полу­чать сооб­ще­ния через команд­ную стро­ку, конеч­но, весе­ло, но толь­ко до опре­де­лен­но­го момен­та. Инте­рес­но, насколь­ко слож­нее сде­лать то же самое из кода.

Ни насколь­ко. Для при­ме­ра я выбрал JavaScript, но любой язык, на кото­рый пор­ти­ро­ван AMQP кли­ент, подо­шёл бы. В NPM есть пакет с гово­ря­щим назва­ни­ем amqplib и это как раз то, что нам понадобится:

Что­бы что-то отпра­вить, нуж­но сна­ча­ла открыть соеди­не­ние и канал:

В amqplib есть малень­кая воз­мож­ность схит­рить — сооб­ще­ние мож­но отпра­вить напря­мую оче­ре­ди, в обход exchange, и этой хит­ро­стью я сей­час и воспользуюсь.

Итак, соеди­не­ние есть, канал есть, оста­лось толь­ко убе­дить­ся, что оче­редь тоже есть:

Даже если оче­ре­ди не было,  assertQueue теперь её создал.

Но что, теперь всё есть. Пора бы что-нибудь и отправить:

Всё! При­мер неболь­шой полу­чил­ся, но его хва­ти­ло, что­бы под­клю­чить­ся к бро­ке­ру, создать оче­редь сооб­ще­ний и поло­жить туда посыл­ку через AMQ протокол.

Что­бы про­чи­тать сооб­ще­ние обрат­но, нуж­но поме­нять в при­ме­ре все­го один метод. В этот раз я при­ве­ду код целиком:

В consume мож­но было бы доба­вить пара­метр  {noAck: false} — не под­твер­ждать полу­че­ние, что­бы оно оста­лось в очереди.

Итак

В этой гигант­ской по моим мер­кам ста­тье мы лишь вкрат­це про­шлись по тому, что уме­ет RabbitMQ. Настоль­ко вкрат­це, что ни одна из фич, о кото­рых я упо­мя­нул вна­ча­ле, тут так и не встре­ти­лась. Но фокус был в дру­гом — хотя RabbitMQ про­сто нафар­ши­ро­ван фича­ми, не все из кото­рых я могу выго­во­рить, поль­зо­вать­ся им очень легко.

Но, с дру­гой сто­ро­ны, писать hello-world на инстру­мен­те, кото­рый может мас­шта­би­ро­вать­ся по обла­кам, тоже чуть-чуть издёв­ка, так что в сле­ду­ю­щий раз мы попро­бу­ем что-нибудь посложнее.