Scalable Microservice Architecture Using RabbitMQ RPC
By: Oded Shimon
Tags: Microservices, Software Development, Python, Software Architecture, Rabbitmq

Introduction

Microservices are the hot thing, in the past few years microservices have seemed to gain momentum as a very common architecture. Innovative and successful companies like Netflix and Amazon have adopted it, and for a good reason.

The main idea of microservices is to separate the application logic into loosely-coupled services, while each service has its own independent business domain. This concept, if performed accurately, solves a lot of problems that less modern architectures, such as monoliths, didn’t manage to solve: services that are easy to scale, easy to deploy, easy to test and the list goes on…



Even so, if I had to pick one reason why this architecture is so popular, I would bet that the reason is the impact on the daily routine of programmers and executives alike. For example, microservices goes hand in hand with the agile methodology. It’s easy to make changes in a single component, which is significant in the way that a programmer can even decide that he is using a different programming language than another developer working on the same system. From the eyes of the executive easy integrations are guaranteed, and in the end everyone is happy.

Microservices & Queues

We’ve talked enough about microservices, let’s talk a little about message queues. Services can communicate with each other in different ways, and we will not review them all. There is no doubt that the use of queues to communicate is also very popular, again for a good reason too. Many times, when I start the task of designing the architecture for a product or a component, I look at the whole stack. I will be a little romantic for a moment and tell you that I believe there is a reason that certain technologies connect with each other, like a chemical interaction that develops over time. A Specific programming language and a database? A particular operating system and a web server? I’m sure you can think of some!



In my humble opinion, this is the very case between message queues and microservices. Queues have a lot of benefits. They make it possible to deal with a temporary downtime of a service — in case there is no consumer messages in a queue will not be consumed, support multiple architectures and can even provide additional features like load balancing and monitoring of the load on each service.

RabbitMQ as a Message Broker

RabbitMQ is a great message-broker, it’s reliable, built for performance, supports multiple protocols, and has a strong community that makes it easy to find documentation and support.



In this article, I will explain how to build a solution based on microservices and queues using the RPC pattern. This is just a one way to do it, and in fact the RabbitMQ website has excellent documentation of 6 different patterns in which you can communicate. Why this pattern? Because I have used it with a real-time system that receives hundreds of thousands of requests per hour and found it elegant, fast, and easy to maintain. Let’s get started!

Remote Procedure Call with RabbitMQ (RPC)

While designing a microservice system, the best practice is to keep each service independent, by means its not dependent on any other microservice.

But in real life the reality is often far more complex. Imagine that you want to build a complete system made entirely out of microservices that communicate with each other via queues (due to all the benefits described above), but in addition, your system needs to provide a Web API that externalizes all the capabilities of your system using HTTP. This need could be necessary for a good architectural reason, like a front-end application that is designed to communicate with the backend through HTTP. That means that this Web API needs to get HTTP messages, hold the connection, trigger one or more microservices by sending a proper message to the RabbitMQ server, wait for the specific response and then send it back to the waiting HTTP client.

Basically, the above example describes a situation where one service has a direct dependency on another service in order to complete its job.

Now that we have understood how the RPC Pattern can be useful, I will explain how to implement it using RabbitMQ built-in features.

Message Properties

Every RabbitMQ message has a properties section that combines the message metadata. This section contains a list of optional properties, although most of them are rarely used, for our purpose we will use two of them:

reply_to — carries the response queue name.

— carries the response queue name. correlation_id — carries the identifier of the sender message.

(message properties example)

It is possible to think of it like sending a letter. In a situation where we do not want to receive a reply to the letter we have sent, we do not have to indicate our address. However, if we want to receive a reply, we must indicate our address. In addition, if we do receive the letter but the letter does not start with an explanation that it is a response letter to the letter that we have sent earlier, we will not know what to do with its content. In this case the “response queue name” is our mailbox address and the Correlation ID is the explanation that it is a response letter to our first letter.

(RPC flow example)

Sample Code

One of the things I like to do the most when I recognize that there is a well-defined set of logic, which once implemented and tested can be used by me or others, is to pack it as a package. That way, I know I do not have to implement this logic over and over, and instead I can just use it with no concern in any project I work on in the future. Therefore, we will implement this functionality as an independent Python package.

The purpose of our package is to provide a single function that allows the user to send an RPC request and receive a response asynchronously as one liner.

Let’s make it reusable — aka package

Note that all the code snippets below (I have named this package “TornadoBunny”) are available on my GitHub account under: