RPC over RabbitMQ
In this post I would like to address a question that I see often in the RabbitMQ mailing list and IRC channel. How to reply to a client request with RabbitMQ? I think this question can be quite tricky for new users when we are not familiar with AMQP. The big deal here is to know about the options that we can use to call exchange.declare and queue.declare. So let’s see how to implement RPC using RabbitMQ and AMQP.
For our purposes we need a server that will get the job requests from a queue. That queue must be bound to an exchange where our client will send the job requests. So far, this is what we are used to do with RabbitMQ. Then we need a reply queue. A place where the responses from the server will go so they can reach our client. Since we can have many clients sending requests to the server, we can’t know in advance the name of those queues, so we will let RabbitMQ assign those names. We also want that once the client is disconnected the queue is removed to keep the server status clean –we don’t want to end with thousands of idle queues in the server–. To achieve this we can declare an anonymous queue as we did in the previous blog post. We need to get the queue name that RabbitMQ assigned to our client and then see how to tell the server that we want the replies into that queue.
This seems like a lot of work… Well, not really if we use the power of AMQP.
According to the AMQP spec there’s a direct exchange with no public name to which every queue must be bound by default using the queue name as routing key. Our server has to use this exchange to reply to its clients, publishing the messages with said routing key. The next piece in our puzzle is the reply-to property from AMQP messages. When our client publishes a request, it will send the queue name via the reply-to property. Once the request is published it will wait and listen into its own anonymous queue. Once the RPC server gets the reply ready, it will send it to the exchange and it will finally arrive to our client. Easy…
Here’s an image to further illustrate the point:
So, let’s see the code now, which is written in Haskell as in the previous post. In this case we will have a server that will hold a Map of users to their surnames. We will query the server for the users surnames, so our request will be the user name that we will input as a parameter when we invoke our client.
Here’s the Code for the client:
As we did in the previous blog post, first we declare an anonymous queue. The server will assign it a random name which we will use later as the replyTo property of our message, which we pass to the RPC server when we create the msg in the where part. Then the code is nearly the same as in the previous example, where we declare a callback for our consumer.
Next is the code for the RPC server:
First we setup the exchange and queue that we will use to receive requests. Then on the rpcServer callback we consume messages extracting from them the request and the replyTo property. We create a fake database to keep track of a couple of users and their surnames and we do a lookup based on the provided user name. Our callback will take care of sending the reply back to the client via RabbitMQ. You can see that the message is published to an exchange with no name using the queue name as the routing key, as we explained before.
Let’s try this in the command line, first start the server and then on type the following on a Terminal window.
Once the RPC server has started, fire up the consumer passing as parameter one of the names that we have in the database:
And that’s it!
I know this example is pretty simple, but I think is enough to illustrate how easy is to implement several kinds of messaging patterns using RabbitMQ and AMQP.