RabbitMQ: A Look into Messaging Queues

RabbitMQ and Messaging Queues

Messaging queues plays a large role in the communication channels of large enterprise systems. A message queue/broker is an added system that stores a queue of messages between multiple systems. 
Message queues will bring general consistency to your architecture by providing a delivery mechanism that ensures the proper delivery of your messages. To integrate message queues into your architecture, we're going to have to re-think how we connect our services together. RabbitMQ is a popular framework for implementing message queues within your system, although there are alternatives such as SQS or MSMQ.

Components

Messaging Queues consists of three main components.

Publisher - This is a system that emits some message within your environment. The message is then stored to the message queue.

The Message Queue - A system that stores the messages in a queue to be consumed by other systems in your environment.

Consumer - A system that takes and processes messages from the message queue.

RabbitMQ introduces a fourth component to the traditional structure called the Exchange, where it decides which message queue to bind the incoming message to based on some configuration.

General Process

Messaging queues are created to facilitate the communication between systems in your environment. The process is started when one system needs to communicate with one or more systems. 

1. The system that wants to initiate the communication is called the Publisher. It begins by publishing its message to the Exchange.
2. The Exchange which then decides which message queue to bind the message to. 
3. When the queue receives the message, it will return a 'Confirm' message back to the publisher notifying it that it successfully received the message. 
4. From there it will stay in the message queue until a consumer that is listening to that queue pulls a message. 
5. When the consumer has successfully processed the message, it will return an 'Ack' message back to the queue so the queue knows it can be removed.

Potential Problems with this Model

When working with networks, we have to make some assumptions that the network will fail at some point and that will cause errors if not anticipated.

Failing a Confirm

The first point of failure is when a Publisher is trying to publish a message to the queue. In the expected path, the system would add the message and receive the confirm response and complete its operation. But there is a chance that the network may go down while the message queue had successfully received the message, and it will not return the confirm response. In this case the Publisher would then see that it did not receive the confirm response and try to load the message into the queue again. This will cause duplicates of the same message to exist within the queue.

Failing an Ack

The second point of failure comes when the Consumer has finished processing a message, and tries to return the Ack response back to the message queue to indicate that it completed. If the Ack response is not received by the queue, then it does not know that the message has been completed, and will retry to send the message. This means that a message can be processed multiple times by the consumer.

Handling Duplication

Both problems mentions comes down to our consumers possibly doing actions multiple times. The system is unable to satisfy the delivery of a message 'exactly once', but it does guarantee 'at least once'. One way of thinking is that instead of specifying an action for the system do, we need to declare a state of the system and let the system decide how to get to that state. That way if the system encounters a duplicate message, it is already in the desired state it will not do the action again.

The Exchange

RabbitMQ introduces a component called the Exchange which is the director of requests. There will be many queues in your system, and the exchange decides which queue a message should be routed to.

The Exchange supports three different strategies:

1. Fan-Out - The message is sent to all queues associated with it
2. Direct - The message will be sent to the queue with a specific label
3. Topic - The message will be sent to all queues that satisfy some constraints

Dealing with Consumer Failure

Inevitably messages will be failed to be consumed by Consumers, whether it results in an error or there are no available Consumers to take it at that time. There are two strategies for dealing with these issues:

1. Delayed Retry - Retry after a specified time
2. Dead Letter Queue - Send messages to a specified queue where it will be stored until it can be inspected for its failure, and then either be deleted or sent back to the original queue.

For delayed retries, RabbitMQ implements a special type of retry, where it can specify up to 28 different levels of delay retry for a message. It implements this functionality by appending the retry level to the key label itself. In this way we only have to manage one queue for retries, instead of multiple.

Load Balancers and Queues

In a traditional scale-able enterprise level application, we would use a Load Balancer to direct traffic to the appropriate servers, and configure your environment to start additional servers as needed to handle the load.

When we start to incorporate Messaging Queues, we are changing the relationship of how connection between servers are established. Now we move the responsibilities of who starts the processing chain to the consumer. Consumers will poll the queue for work. What we are missing is the piece that can manage the environment and examine the health of all the systems and determine the scaling requirements. There are tools that orchestrates these scaling issues, the popular tooling now being Kubernetes, which will be gone over in a future post. But what it essentially does is act as a controller of sub-systems and can get the status of all the machines and determine what it needs to do and can create the necessary instances  and allocate processing power to meet certain criteria.

Comments

Popular posts from this blog

Uncle Bob's Clean Architecture

C4 Model: Describing Software Architecture

Running RabbitMQ with Docker