Amazon MQ RabbitMQ: A Reliable Messaging Solution for Your .NET Projects
Tags: dotnet, rabbitmq, microservices, aws
Published: 2024-02-05 07:19:54

RabbitMQ is a powerful open-source message broker facilitating communication between systems or applications.

It ensures seamless data exchange by enabling asynchronous messaging, making it an essential tool for building scalable and resilient distributed systems.

{% embed https://youtu.be/4DDAGsXrNAQ %}

In this post, we will learn how to set up RabbitMQ and start using it from a .NET application to send and receive messages.

## Setting Up RabbitMQ

RabbitMQ can be hosted through self-hosted deployments, major cloud providers, containerization with Docker and Kubernetes, managed RabbitMQ providers, etc, offering a wide range of options from complete control to fully managed solutions.

For this post, I will use Amazon MQ, a managed message broker service that supports ActiveMQ and RabbitMQ engine types.

However, you can use [one of the various options that RabbitMQ provides](https://www.rabbitmq.com/download.html) to host your instance, including a local Docker instance.

### Creating RabbitMQ broker on Amazon MQ

[Amazon MQ](https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/welcome.html) is a fully managed message broker service on AWS that supports popular messaging protocols, providing compatibility, reliability, scalability, and security for distributed applications.

Amazon MQ supports Apache ActiveMQ and RabbitMQ engine types.

To create a RabbitMQ message broker, navigate to Amazon MQ in the AWS Console.

Create a new broker and choose RabbitMQ as the broker engine type, as shown below.

![Create a new Amazon MQ broker using the RabbitMQ broker type.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xbky1e7yly03p29erbi8.png)

Create a new Amazon MQ broker using the RabbitMQ broker type.

It then prompts you to choose the deployment mode.

* **Single-instance broker** → one broker in one Availability Zone, mainly used for development and testing.
    
* **Cluster deployment** → logical grouping of three RabbitMQ broker nodes behind a Network Load Balancer, each sharing users, queues, and a distributed state across multiple Availability Zones    

![Choose the deployment mode for Amazon MQ Rabbit broker instance - choose between single instance and cluster.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nzosjf6mt7bpw3pgnmop.png)

Choose the deployment mode for Amazon MQ Rabbit broker instance - choose between single instance and cluster.

For purposes of this blog post, I am using a Single instance. However, choose Cluster deployment if you are setting up a production workload.

Once done you can configure the broker name, the instance type, username/password and other advanced configuration required.

![Configure RabbitMQ setting in Amazon MQ.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t5qthxg6jr70x264pydl.png)

Configure RabbitMQ setting in Amazon MQ.

Review and create the RabbitMQ broker instance.

![RabbitMQ broker instance up and running and the details as from the AWS console.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1f3rr0t14iqwxu0ekn8q.png)

RabbitMQ broker instance up and running and the details as from the AWS console.

## Creating .NET Application To Send/Receive Messages From RabbitMQ

RabbitMQ is a message broker.

This means you would typically have a sender/producer and a receiver/consumer when building applications on it. The sender sends messages, and the receiver receives and processes them.

To start using the RabbitMQ broker instance set up above, let's first create two console applications - Send and Receive.

To connect and send/receive messages from RabbitMQ, we will use a NuGet package - [RabbitMQ.Client](https://www.nuget.org/packages/RabbitMQ.Client).

Below is a Powershell script to create the Send and Receive .NET console applications and add the required NuGet packages.

It creates a solution file, creates a Send console app with the start-up file as `Send.cs` , adds the NuGet package. It also creates a Receive console app with the start-up file `Receive.cs` and the required packages.

```powershell
# Step 1: Create a new .NET solution
dotnet new sln -n rabbitmq-hello-world

# Step 2: Create the 'Send' console application
dotnet new console -n Send
dotnet add Send/Send.csproj package RabbitMQ.Client
dotnet sln rabbitmq-hello-world.sln add Send/Send.csproj
mv Send/Program.cs Send/Send.cs

# Step 3: Create the 'Receive' console application
dotnet new console -n Receive
dotnet add Receive/Receive.csproj package RabbitMQ.Client
dotnet sln rabbitmq-hello-world.sln add Receive/Receive.csproj
mv Receive/Program.cs Receive/Receive.cs
```

## Core Connection Concepts With RabbitMQ

The two core concepts that you need to know in terms of connecting with RabbitMQ are

* **Connection:** This represents the TCP connection between your application and the RabbitMQ broker.
    
* **Channels:** A virtual connection within a connection**.** Used for specific tasks like publishing, consuming, and declaring queues and exchanges. Channels help organize and optimize communication between your application and the broker.
    

Since it's undesirable to have multiple TCP connections open at the same time from our applications to RabbitMQ, we can create one Connection and then use Channels to open lightweight connections with the broker.

Channels in RabbitMQ support efficient multiplexing, isolation, resource management, concurrency, and performance optimization while still sharing the same underlying TCP connection.

## Connecting .NET Application with RabbitMQ

To connect to RabbitMQ, let's use the `ConnectionFactory` from the `RabbitMQ.Client` NuGet package to create a new TCP Connection.

```csharp
var factory = new ConnectionFactory
{
    Uri = new Uri("amqps://b-71e1955a-1942-47e2-9e49-xxxxxxxxxxxx.mq.ap-southeast-2.amazonaws.com"),
    Port = 5671,
    UserName = "<USERNAME FROM CONFIGURATION FILE>",
    Password = "<PASSWORD FROM CONFIGURATION FILE>"
};
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
```

The above code specifies the `Uri` to connect to the RabbitMQ instance in Amazon MQ and specify the username/password we used when configuring the RabbitMQ broker instance.

Using the Connection, it creates a Channel (referred to as Model in .NET speak), using the `CreateModel` function.

The Channel instance can then be used to send or receive messages from the RabbitMQ instance.

### Creating a Queue in RabbitMQ From .NET Application

We first need to create a Queue to send or receive messages from RabbitMQ.

A Queue in RabbitMQ is a named message buffer that temporarily stores messages sent by producers and delivers them to consumers.

```csharp
channel.QueueDeclare(queue: "hello",
    durable: false,
    exclusive: false,
    autoDelete: false,
    arguments: null);
```

The above code uses the `QueueDeclare` function on the channel instance to create a simple queue.

Creating a Queue is an idempotent operation.

It means a new Queue is created only if it does not already exist. So it's safe to have this same code in both our Send/Receive applications since we cannot guarantee which one will run first, and we need the Queue to exist regardless of the order in which they are run.

We can now use this Queue to send/receive messages from our .NET application.

### Sending Messages to RabbitMQ From .NET Application

To send a message to RabbitMQ, we can use the `BasicPublish` method on the channel instance.

Below we get the message text to send from the console and send it as the message body.

```csharp
string? message = null;
do
{
    Console.WriteLine("Enter Message. Press [enter] to exit.");
    message = Console.ReadLine();
    if (!string.IsNullOrEmpty(message))
        SendMessage(message, channel);
} while (!string.IsNullOrEmpty(message));

void SendMessage(string s, IModel channel)
{
    var body = Encoding.UTF8.GetBytes(s);

    channel.BasicPublish(exchange: string.Empty,
        routingKey: "hello",
        basicProperties: null,
        body: body);
    Console.WriteLine($" [x] Sent {s}");
}
```

Note that we specify the `exchange` as empty and `routingKey` as the queue name. This uses the [default exchange](https://www.rabbitmq.com/tutorials/amqp-concepts.html#exchange-default) that's available as part of RabbitMQ.

In a future blog post, we will learn more about Exchanges and the different types.

### Receive Messages From RabbitMQ From .NET Application

With the message published successfully we are now ready to add in our first consumer/receiver for this message.

The `channel` instance provides a `BasicConsumer` method to register a consumer instance to a queue.

```csharp
Console.WriteLine(" [*] Waiting for messages.");

var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
    var body = ea.Body.ToArray();
    var message = Encoding.UTF8.GetString(body);
    Console.WriteLine($" [x] Received {message}");
};
channel.BasicConsume(queue: "hello",
    autoAck: true,
    consumer: consumer);

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

The RabbitMQ.Client NuGet package provides a `EventingBasicConsumer` class we can use to handle the message processing.

The above code sets up the `Received` event handler with the function to process the message, in this case, writing the message body to the console.

![Send and Receive applications in action, working with Amazon MQ RabbitMQ.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2woa8dtkbpmeet5owdgg.gif)

Send and Receive applications in action, working with Amazon MQ RabbitMQ.

`Send >` 'Have a great day 😀'