Before diving into the implementation, you'll need to install the RabbitMQ client library from NuGet (version 5.1.2 recommended). Additionally, you'll need a running RabbitMQ server instance deployed on a machine accessible from your application.
This guide covers two primary messaging patterns with RabbitMQ. There's one important caveat to be aware of: avoid declaring multiple consumers with the same queue name. When multiple consumers share an identical queue name, the broker distributes messages in a round-robin fashion—meaning each consumer receives messages alternately rather than simultaneously. To receive the same message on multiple endpoints, you should use the broadcast pattern instead.
Point-to-Point Queue Pattern
In this pattern, messages sent to a queue are delivered to exactly one consumer. This is ideal for task distribution and load balancing scenarios.
Publisher Implementation:
using RabbitMQ.Client;
using System;
using System.Text;
class MessagePublisher
{
static void Main(string[] args)
{
string queueIdentifier = "processing_queue";
ConnectionFactory factory = new ConnectionFactory
{
AutomaticRecoveryEnabled = true,
HostName = "your-mq-server-ip",
UserName = "your-username",
Password = "your-password"
};
using (IConnection serverConnection = factory.CreateConnection())
using (IModel channel = serverConnection.CreateModel())
{
channel.QueueDeclare(queueIdentifier, durable: true, exclusive: false, autoDelete: false, arguments: null);
IBasicProperties messageProperties = channel.CreateBasicProperties();
messageProperties.DeliveryMode = 1; // Non-persistent
string payload = "Processing message content";
channel.BasicPublish(
exchange: "",
routingKey: queueIdentifier,
basicProperties: messageProperties,
body: Encoding.UTF8.GetBytes(payload)
);
}
}
}
Consumer Implementation:
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
class MessageConsumer
{
static void Main(string[] args)
{
string queueIdentifier = "processing_queue";
ConnectionFactory factory = new ConnectionFactory
{
AutomaticRecoveryEnabled = true,
HostName = "your-mq-server-ip",
UserName = "your-username",
Password = "your-password"
};
IConnection serverConnection = factory.CreateConnection();
IModel channel = serverConnection.CreateModel();
channel.QueueDeclare(queueIdentifier, durable: true, exclusive: false, autoDelete: false, arguments: null);
EventingBasicConsumer messageHandler = new EventingBasicConsumer(channel);
messageHandler.Received += HandleIncomingMessage;
channel.BasicConsume(
queue: queueIdentifier,
autoAck: true,
consumer: messageHandler
);
Console.ReadKey();
channel.Dispose();
serverConnection.Dispose();
}
private static void HandleIncomingMessage(object sender, BasicDeliverEventArgs eventArgs)
{
try
{
byte[] messageBody = eventArgs.Body;
string messageContent = Encoding.UTF8.GetString(messageBody);
Console.WriteLine($"Received: {messageContent}");
}
catch (Exception processingError)
{
Console.WriteLine($"Error processing message: {processingError.Message}");
}
}
}
Fanout Broadcast Pattern
The broadcast pattern uses a fanout exchange to deliver messages to all bound queues simultaneously. When you declare an exchange with the Fanout type and bind multiple queues to it, every message published to that exchange reaches all connected queues.
Publisher Implementation:
using RabbitMQ.Client;
using System.Text;
class BroadcastPublisher
{
static void Main(string[] args)
{
string exchangeIdentifier = "notifications_exchange";
string routingKey = "";
ConnectionFactory connectionFactory = new ConnectionFactory
{
UserName = "username",
Password = "password",
HostName = "mq-server-address"
};
using (IConnection connection = connectionFactory.CreateConnection())
using (IModel channel = connection.CreateModel())
{
string messageContent = "Broadcast notification message";
channel.BasicPublish(
exchange: exchangeIdentifier,
routingKey: routingKey,
basicProperties: null,
body: Encoding.UTF8.GetBytes(messageContent)
);
}
}
}
Consumer Implementation:
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
class BroadcastConsumer
{
static void Main(string[] args)
{
string queueIdentifier = "notification_queue_1";
string exchangeIdentifier = "notifications_exchange";
string bindingKey = string.Empty;
ConnectionFactory connectionFactory = new ConnectionFactory
{
UserName = "username",
Password = "password",
HostName = "mq-server-ip"
};
IConnection connection = connectionFactory.CreateConnection();
IModel channel = connection.CreateModel();
channel.ExchangeDeclare(exchangeIdentifier, ExchangeType.Fanout, durable: true, autoDelete: false, arguments: null);
channel.QueueDeclare(queueIdentifier, durable: false, exclusive: false, autoDelete: false, arguments: null);
channel.QueueBind(queueIdentifier, exchangeIdentifier, bindingKey, null);
EventingBasicConsumer eventConsumer = new EventingBasicConsumer(channel);
eventConsumer.Received += (channelContext, deliveryEvent) =>
{
string receivedMessage = Encoding.UTF8.GetString(deliveryEvent.Body);
Console.WriteLine($"[{queueIdentifier}] Received broadcast: {receivedMessage}");
channel.BasicAck(deliveryEvent.DeliveryTag, multiple: true);
};
channel.BasicConsume(queue: queueIdentifier, autoAck: false, consumer: eventConsumer);
Console.WriteLine("Listening for broadcast messages...");
Console.ReadLine();
}
}
The key difference between these patterns lies in message distribution: queue mode ensures each message reaches exactly one consumer, while fanout mode delivers every message to all registered queues. Choose the pattern that aligns with your application's requirements for message consumption and fault tolerance.