RabbitMQ is an example of full blown Message Queue that somehow remained simple to use. Unlike ZeroMQ, which is embeddable into the services that use it, RabbitMQ is a broker. It’s an intermediary messaging service with own users, permissions, encryption, configurable durability and delivery acknowledgements, clustering, high availability, and bazillion of other features you might never need. RabbitMQ is built on top of Erlang and inherits its known resilience with compatibility to virtually any OS.
In the following article we’ll try to get a sense of how messaging with RabbitMQ feels like. I’ve chosen Ubuntu (in a Docker container) as a platform, but it could’ve been anything else.
Installation
Installation is not in the focus here, especially when official documentation is so good, but the simplest method of getting it up and running is through rabbitmq Docker container.
1 2 |
$ docker run -ti rabbitmq bash # root@714dbe064eef:/# |
This will get you fully configured RabbitMQ server, which only waits to be started:
1 2 |
$ service rabbitmq-server start #[ ok ] Starting message broker: rabbitmq-server. |
If you’re not familiar with Docker, installing RabbitMQ on real machine is also not that hard. This is how it looks for Ubuntu:
1 2 3 4 |
echo 'deb http://www.rabbitmq.com/debian/ testing main' | sudo tee /etc/apt/sources.list.d/rabbitmq.list wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | sudo apt-key add - sudo apt-get update sudo apt-get install rabbitmq-server |
It won’t be harder on other systems, but I’d highly recommend investing some time in learning Docker, as it’s an amazing tool for experimenting with such kind of stuff. Just pull the image of already configured software (RabbitMQ, Redis, Elasticsearch, Jenkins, and ton of others), play with it and throw away when you’ve done. That’s brilliant.
Sending and receiving messages with rabbitmqadmin
You don’t have to write any code to start sending and receiving messages. RabbitMQ comes with management plugin which has rabbitmqadmin – a tool capable of creating message queues, sending messages, querying queue status, etc. Management plugin is disabled by default, so let’s enable it first.
Getting rabbitmqadmin ready
1 2 3 4 5 6 7 8 9 10 |
$ rabbitmq-plugins enable rabbitmq_management #The following plugins have been enabled: # mochiweb # webmachine # rabbitmq_web_dispatch # amqp_client # rabbitmq_management_agent # rabbitmq_management # #Applying plugin configuration to rabbit@fb252aa8ddbd... started 6 plugins. |
..and it’s done. It also worth adding rabbitmqadmin location to PATH variable, or at least creating a link to it in /usr/local/bin/.
1 2 |
find /var/lib/rabbitmq -name rabbitmqadmin | xargs -I{} sudo ln -s {} /usr/local/bin/rabbitmqadmin sudo chmod +x /usr/local/bin/rabbitmqadmin |
Cool! Now we can do something with it.
Messing with queues and messages
RabbitMQ is a broker that hosts message queues and in your average distributed application it sits somewhere in the middle:
First, let’s see what queues it has by default:
1 2 |
$ rabbitmqadmin list queues # No items |
Not much. But it’s something we can easily fix:
1 2 3 4 5 6 7 8 |
$ rabbitmqadmin declare queue name=demoqueue durable=false #queue declared $ rabbitmqadmin list queues +-----------+----------+ | name | messages | +-----------+----------+ | demoqueue | 0 | +-----------+----------+ |
We created a queue called “demoqueue” which is not durable, so as soon as RabbitMQ server shuts down, all non-delivered messages will be lost. Talking about the messages, let’s create one!
1 2 3 4 5 6 7 8 9 |
$ rabbitmqadmin publish exchange=amq.default routing_key=demoqueue \ payload="Behold! This is the message" # Message published $ rabbitmqadmin list queues +-----------+----------+ | name | messages | +-----------+----------+ | demoqueue | 1 | +-----------+----------+ |
Send message command looked more complicated than it should, so I think I owe you some explanations.
AMQP detour
There’s emerging standard for message queue protocols called AMQP (Advanced Message Queuing Protocol). According to this standard, there should be one more actor between message publisher and message queue called exchange. It’s like a mailbox – before your letter gets to the post office, it should hang in somewhere for a while. Publisher sends the message to exchange, while exchange’s type and its bindings define where it goes next.
For example, if exchange type is “fanout” (one of predefined types), then every message that hits the exchange will be broadcasted to all message queues bound to it.
On the other hand, there’s “direct” exchange, which routes messages by certain routing key. It’s like “OK, exchange, here’s message queue X I bind to you. Whenever you get a message with routing key Y, forward it to X”.
RabbitMQ conforms AMQP standard, so it also comes with predefined amq.default exchange, which is, in fact, “direct”-type exchange that every MQ is automatically bound to, using their name as a routing key. So this line probably makes more sense now:
1 |
$ rabbitmqadmin publish exchange=amq.default routing_key=demoqueue payload="..." |
Finally, let’s receive (“consume” in RabbitMQ terminology) that message back:
1 2 3 4 5 6 |
$ rabbitmqadmin get queue=demoqueue requeue=true +-------------+----------+---------------+-----------------------------+---------------+------------------+------------+-------------+ | routing_key | exchange | message_count | payload | payload_bytes | payload_encoding | properties | redelivered | +-------------+----------+---------------+-----------------------------+---------------+------------------+------------+-------------+ | demoqueue | | 0 | Behold! This is the message | 27 | string | | False | +-------------+----------+---------------+-----------------------------+---------------+------------------+------------+-------------+ |
requeue=true argument indicates that message should be put back into the queue after it’s read, so we can play with it later.
Sending and receiving messages with NodeJS
So we’ve learned that RabbitMQ is a message broker we can send to and receive messages from via command line tools. How sweet. I wonder how hard it’s to do that programmatically. Spoiler – not hard at all.
I’ve chosen JavaScript and NodeJS for the demo, as this is the quickest way for me to get it up and running, but any language with AMQP library would do.
NPM has a package called amqplib and that’s exactly what we need:
1 |
npm install amqplib |
Before we could send or receive messages, we need to connect to RabbitMQ server and open a channel:
1 2 3 4 |
const amqp = require('amqplib'); amqp.connect('amqp://localhost') .then(connection => connection.createChannel()) |
amqplib comes with Promise API, but they have callback-style syntax as well. What’s more, it also allows publishing messages directly to MQs, bypassing exchange – feature I’m going to exploit in a few seconds.
Now, having connection established, let’s make sure the queue we’re going to publish to actually exists:
1 2 3 4 |
//.... .then(channel, function () { return channel.assertQueue('demoqueue', {durable: false}); }) |
The beauty of assertQueue is that if queue doesn’t exist, it will be created. Finally, channel is ready, queue is ready – time to send something:
1 2 3 4 5 6 |
.then(function () { return channel.sendToQueue( 'demoqueue', new Buffer('Behold one more time!') ); }); |
And… that’s it. That code is enough to create MQ and send message to it using AMQ Protocol.
NodeJS program for reading from queue is almost identical to one that sends it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
const amqp = require('amqplib'); const queueName = 'demoqueue'; amqp.connect('amqp://localhost') .then(connection => connection.createChannel()) .then(function (channel) { return channel .assertQueue(queueName, {durable: false}) .then(function () { return channel.consume(queueName, function (msg) { console.log(`message received ${msg.content.toString()}`); }); }); }) |
We could’ve add {noAck: false} parameter to consume method, so messages wouldn’t be acknowledged as read and therefore would stay in the queue (like reading with rabbitmqadmin and requeue=true flag), but that’s too much for one day.
Summary
In this article we barely scratched the surface of what RabbitMQ can. In fact, none of the features I mentioned in the beginning were used. But the point I was trying to make is that being a feature rich tool that is capable of handling extremely complex scenarios doesn’t mean it also should be hard to use. In contrary, examples above were quite trivial.
But it’s a shame to use such a tool for hello-world applications. Let’s try something more complex next time.
2 thoughts on “Quick intro to RabbitMQ”