Hi devs,
RabbitMQ is one of the most popular message brokers, enabling communication between services in a distributed system. It supports various messaging patterns, such as work queues, publish/subscribe, and RPC. In this post, I’ll demonstrate how to set up RabbitMQ and use it to enable communication between three microservices: Order Service, Inventory Service, and Notification Service.
Why RabbitMQ?
RabbitMQ allows microservices to:
- Decouple communication: Services don't need to know about each other.
- Increase reliability: Messages are persisted until delivered.
- Improve scalability: Multiple consumers can process messages concurrently.
- Support multiple protocols: AMQP, STOMP, MQTT, etc.
Architecture Overview
In this example:
-
Order Service publishes an
OrderPlaced
message to a RabbitMQ exchange. - Inventory Service subscribes to the exchange to update stock.
- Notification Service subscribes to the exchange to send order confirmation notifications.
Prerequisites
-
Install RabbitMQ:
- Using Docker:
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:management
- Access the RabbitMQ management UI at
http://localhost:15672
(default username/password:guest/guest
).
- Install the
RabbitMQ.Client
package in your .NET projects:
dotnet add package RabbitMQ.Client
Step-by-Step Implementation
Step 1: Define a Shared Order Model
public class Order
{
public int Id { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
public decimal TotalPrice { get; set; }
}
This model will represent the order data shared between services.
Step 2: Create the Order Service (Producer)
The Order Service publishes messages to the RabbitMQ exchange.
using RabbitMQ.Client;
using System.Text;
using System.Text.Json;
public class OrderService
{
private readonly IModel _channel;
public OrderService()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
var connection = factory.CreateConnection();
_channel = connection.CreateModel();
// Declare an exchange and a queue
_channel.ExchangeDeclare(exchange: "orders_exchange", type: ExchangeType.Fanout);
}
public void PlaceOrder(Order order)
{
var message = JsonSerializer.Serialize(order);
var body = Encoding.UTF8.GetBytes(message);
_channel.BasicPublish(exchange: "orders_exchange", routingKey: "", basicProperties: null, body: body);
Console.WriteLine($"Order placed: {order.ProductName}");
}
}
// Usage
var orderService = new OrderService();
var order = new Order { Id = 1, ProductName = "Laptop", Quantity = 1, TotalPrice = 1500.00m };
orderService.PlaceOrder(order);
Here, the Order Service
publishes an OrderPlaced
event to the orders_exchange
.
Step 3: Create the Inventory Service (Consumer)
The Inventory Service listens to the orders_exchange
and updates stock.
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;
using System.Text.Json;
public class InventoryService
{
public void Start()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
var connection = factory.CreateConnection();
var channel = connection.CreateModel();
// Declare the exchange and queue
channel.ExchangeDeclare(exchange: "orders_exchange", type: ExchangeType.Fanout);
var queueName = channel.QueueDeclare().QueueName;
channel.QueueBind(queue: queueName, exchange: "orders_exchange", routingKey: "");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
var order = JsonSerializer.Deserialize<Order>(message);
Console.WriteLine($"Inventory updated for Product: {order.ProductName}, Quantity: {order.Quantity}");
};
channel.BasicConsume(queue: queueName, autoAck: true, consumer: consumer);
Console.WriteLine("Inventory Service is running...");
}
}
// Usage
var inventoryService = new InventoryService();
inventoryService.Start();
Step 4: Create the Notification Service (Consumer)
The Notification Service listens to the orders_exchange
and sends notifications.
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;
using System.Text.Json;
public class NotificationService
{
public void Start()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
var connection = factory.CreateConnection();
var channel = connection.CreateModel();
// Declare the exchange and queue
channel.ExchangeDeclare(exchange: "orders_exchange", type: ExchangeType.Fanout);
var queueName = channel.QueueDeclare().QueueName;
channel.QueueBind(queue: queueName, exchange: "orders_exchange", routingKey: "");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
var order = JsonSerializer.Deserialize<Order>(message);
Console.WriteLine($"Notification sent for Order ID: {order.Id}, Product: {order.ProductName}");
};
channel.BasicConsume(queue: queueName, autoAck: true, consumer: consumer);
Console.WriteLine("Notification Service is running...");
}
}
// Usage
var notificationService = new NotificationService();
notificationService.Start();
How It All Works
-
Order Service publishes an
OrderPlaced
event to the RabbitMQorders_exchange
. - RabbitMQ routes the message to all bound queues.
- Inventory Service and Notification Service consume the message and process it independently.
Testing the System
- Run the Inventory Service and Notification Service.
- Place an order using the Order Service.
- Observe the logs for the inventory update and notification.
Benefits of Using RabbitMQ in Microservices
- Decoupled Communication: Producers and consumers don’t need direct knowledge of each other.
- Scalable: Add more consumers to process messages faster.
- Reliable Delivery: Messages are persisted and retried if delivery fails.
- Flexible Messaging Patterns: Work queues, publish/subscribe, RPC, etc.
Conclusion
RabbitMQ is a powerful message broker that simplifies communication in microservices. By using the Fanout Exchange pattern, we ensured that multiple services could respond to the same event independently.
Keep coding!
Top comments (0)