← Back to Blog
Backend Development

Using RabbitMQ with .NET: Message Queues, Producers, and Consumers

nemostormJanuary 15, 20268 min read

Using RabbitMQ with .NET: Message Queues, Producers, and Consumers

Introduction

RabbitMQ is a battle-tested message broker used to decouple services, smooth traffic spikes, and enable asynchronous processing. This article shows the core concepts and practical examples for using RabbitMQ in .NET applications — both with the low-level `rabbitmq.client` library and an example using MassTransit.

Core Concepts

  • Exchange: receives messages from producers and routes them to queues.
  • Queue: stores messages until consumers process them.
  • Binding: link between an exchange and a queue, possibly with a routing key.
  • Routing Key: used by exchanges (direct, topic) to route messages.
Use durable exchanges/queues and persistent messages for reliability.

Quick setup

Install the official client:

dotnet add package RabbitMQ.Client

Or use MassTransit for higher-level abstractions:

dotnet add package MassTransit
dotnet add package MassTransit.RabbitMQ

Basic producer (RabbitMQ.Client)

using RabbitMQ.Client;
using System.Text;

var factory = new ConnectionFactory() { HostName = "localhost" }; using var connection = factory.CreateConnection(); using var channel = connection.CreateModel();

channel.ExchangeDeclare("my-exchange", ExchangeType.Direct, durable: true); channel.QueueDeclare("my-queue", durable: true, exclusive: false, autoDelete: false); channel.QueueBind("my-queue", "my-exchange", "my-key");

var message = "Hello RabbitMQ from .NET"; var body = Encoding.UTF8.GetBytes(message);

var props = channel.CreateBasicProperties(); props.Persistent = true; // mark message persistent

channel.BasicPublish(exchange: "my-exchange", routingKey: "my-key", basicProperties: props, body: body); Console.WriteLine("[x] Sent {0}", message);

Basic consumer (RabbitMQ.Client) - with manual acks

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;

var factory = new ConnectionFactory() { HostName = "localhost" }; using var connection = factory.CreateConnection(); using var channel = connection.CreateModel();

channel.QueueDeclare("my-queue", durable: true, exclusive: false, autoDelete: false);

var consumer = new EventingBasicConsumer(channel); consumer.Received += (model, ea) => { var body = ea.Body.ToArray(); var message = Encoding.UTF8.GetString(body); try { Console.WriteLine("[x] Received {0}", message); // process message

// ack after successful processing channel.BasicAck(ea.DeliveryTag, multiple: false); } catch (Exception ex) { Console.Error.WriteLine("Processing failed: " + ex.Message); // optionally reject and requeue or send to DLX channel.BasicNack(ea.DeliveryTag, multiple: false, requeue: false); } };

// limit unacked messages to improve fairness channel.BasicQos(prefetchSize: 0, prefetchCount: 10, global: false); channel.BasicConsume(queue: "my-queue", autoAck: false, consumer: consumer);

Console.WriteLine("Consumer started. Press [enter] to exit."); Console.ReadLine();

Using MassTransit for simplicity

MassTransit provides a friendly abstraction over transports and message contracts.

// Program.cs
builder.Services.AddMassTransit(x =>
{
    x.UsingRabbitMq((context, cfg) =>
    {
        cfg.Host("rabbitmq://localhost", h =>
        {
            h.Username("guest");
            h.Password("guest");
        });

cfg.ReceiveEndpoint("orders-queue", e => { e.PrefetchCount = 16; e.ConfigureConsumer(context); }); }); x.AddConsumer(); });

// consumer public class OrderConsumer : IConsumer { public async Task Consume(ConsumeContext context) { var msg = context.Message; // handle message await Task.CompletedTask; } }

MassTransit handles retries, middleware, and sagas out of the box which reduces boilerplate.

Best practices

  • Use durable exchanges/queues and persistent messages for durability.
  • Set proper QoS/prefetch to limit unacknowledged messages.
  • Implement idempotent consumers to handle redelivery.
  • Use dead-letter exchanges (DLX) for poison messages and retries.
  • Keep message sizes small; store large payloads in object storage and send references.
  • Monitor connection/channel health and use automatic recovery or reconnection strategies.
  • Prefer using a framework like MassTransit or EasyNetQ if you want higher-level patterns (retries, outbox, sagas).

Troubleshooting and monitoring

  • Use rabbitmqctl and management UI for queue states and consumers.
  • Watch for high ready/unaligned message counts — indicates slow consumers.
  • Track message rates, connection churn, and resource usage (memory, disk).

Conclusion

RabbitMQ is a powerful tool for building resilient distributed systems. In .NET, you can start with the official client for complete control or use MassTransit for productivity and advanced patterns. Pay attention to durability, acknowledgements, prefetch, and idempotency to build reliable message-driven systems.

csharp