I’ve previously spoken and written quite extensively about the pub/sub pattern using message brokers such as GCP and Azure. I’ve also posted a few articles about Rabbit MQ.
In this post, I’d like to cover the Rabbit MQ concept of pub/sub.
The Concept
Most message brokers broadly support two types of message exchange. The first type is a queue: that is, a single, persistent list of messages that can be read by one, or multiple consumers. The use case I usually use for this is sending e-mails: imagine you have a massive amount of e-mails to send: write them all to a queue, and then set 3 or 4 consumers reading the queue and sending the mails.
The second type is publish / subscribe, or pub/sub. This is, essentially, the concept that each consumer has its own private queue. Imagine that you want to notify all the applications in your system that a sales order has been raised: each interested party would register itself as a consumer and, when a message is sent, they would all receive that message. This pattern works well for distributed systems.
As I said, most message brokers broadly support these two concepts, although annoyingly, in different ways and with different labels. Here, we’ll show how RabbitMQ deals with this.
Setting up RabbitMQ
Technology has moved on since the last time I wrote about installing and running it. The following docker command should have you set-up in a couple of seconds:
docker run --rm -it --hostname my-rabbit -p 15672:15672 -p 5672:5672 rabbitmq:3-management
Once it’s running, you can view the dashboard here. If you haven’t changed anything, the default username / password is guest / guest.
Receiver
Before we get into any actual code, you’ll need to install the Rabbiq MQ Client NuGet Package.
For pub/sub, the first task is to set-up a receiver. The following code should do that for you:
var factory = new ConnectionFactory() { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
channel.ExchangeDeclare("SalesOrder", ExchangeType.Fanout);
var result = channel.QueueDeclare("OrderRaised", false, false, false, null);
string queueName = result.QueueName;
channel.QueueBind(queueName, "SalesOrder", "");
Console.WriteLine(result);
EventingBasicConsumer consumer = new EventingBasicConsumer(channel);
consumer.Received += Consumer\_Received;
channel.BasicConsume(queueName, true, consumer);
Console.WriteLine("Receiving...");
Console.ReadLine();
static void Consumer\_Received(object sender, BasicDeliverEventArgs e)
{
var body = e.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(message);
}
In the code above, you’ll see that we first set-up an exchange called SalesOrder, and we tell that exchange that it’s a Fanout exchange.
We then declare a queue, and bind it to the exchange - that is, it will receive messages sent to that exchange. Notice that we receive from the queue.
Finally, we set-up the consumer, and tell it what to do when a message is received (in this case, just output to the console window).
Sender
For the sender, the code is much simpler:
static void SendNewMessage(string message)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
channel.ExchangeDeclare("SalesOrder", ExchangeType.Fanout);
channel.BasicPublish("SalesOrder", "", false, null, Encoding.UTF8.GetBytes(message));
}
Notice that we don’t have any concept of the queue here, we simply publish to the exchange - what happens after that is no concern of the publisher.
Summary
I keep coming back to Rabbit - especially for demos and concepts, as it runs locally easily, and has many more options than the main cloud providers - at least in terms of actual messaging capability. If you’re just learning about message brokers, Rabbit is definitely a good place to start.