RabbitMQ Message Queue using .NET Core 6 Web API

We are going to discuss RabbitMQ message queue and its implementation using .NET Core 6 API as a message producer and console application as a message consumer.

work schedule

  • Introduction to RabbitMQ
  • Benefits of using RabbitMQ
  • Implementation of RabbitMQ in .NET Core 6

prerequisites

  • visual studio 2022
  • docker desktop
  • .NET Core 6 SDK

Introduction to RabbitMQ

  • RabbitMQ acts as a middleware when using multiple microservices.
  • It is open-source message broker software, and is sometimes referred to as message-oriented middleware.
  • It is written in the Erlang programming language.
  • RabbitMQ is used to reduce the load and delivery time of our web applications when some resources have taken too long to process the data.

As you can see in the above diagram, there is a producer which sends a message to the RabbitMQ server and the server will store that message inside the queue in a FIFO manner.

The producer sent the message to the queue so that there can be multiple consumers who want the message produced by the producer. In that case, consumers subscribe to the message and receive that message from the message queue, as you can see in the above diagram.

In this section, we will take an eCommerce site as an example to help us understand.

Many microservices are running in the background when we are using eCommerce website. There is one service that takes care of order details, and another service that takes care of payment details and receipts.

Let’s say we place an order at the time the order service starts, and process our order. After taking the order details, it will send the data to the payment service, which takes the money and sends the payment receipt to the end users.

In that case, there might be a possibility of some technical issue with the payment service. If the user does not receive a payment receipt due to this, the user is affected, and connects to the support team to attempt to retrieve the status of the order.

Also, there could be another scenario on the (consumer) side. In this case, due to some technical issue, the user exits the application while the payment is in process, but does not receive any receipt details once the payment is successfully processed from the back-end services.

Therefore, in these scenarios, RabbitMQ plays a very important role in maintaining messages in the message queue. When the consumer goes online, they receive that order receipt message from the message queue, which is created by the producer without affecting the web application.

All these examples are for understanding purpose only. There can be many scenarios in which RabbitMQ plays a huge role. When using multiple microservices, sometimes RabbitMQ is used for load balancing and many other purposes.

Benefits of using RabbitMQ

There are lots of benefits of using a message broker to send data to the consumer. We will review two of them here.

1. High Availability

When multiple microservices are used by one application – and meanwhile, one microservice is stopped for technical reasons – our message is never lost. It persists in RabbitMQ server, and after some time, when our service starts working again, it will connect to RabbitMQ and take pending message smoothly.

2. Scalability

When we use RabbitMQ, our application does not depend on only one server and virtual machine to process the request. When our server is shutting down, we shift our application load to another server that has similar services running in the background.

RabbitMQ implementation with .NET Core 6

Let’s start with a practical implementation of RabbitMQ using .NET Core 6 Web API as producer and console application as consumer.

step 1

Create a .NET Core API project.

step 2

Configure your project.

step 3

Please provide additional information about your project.

step 4

Here is the project structure of the builder application.

Step 5

Install some NuGet packages.

Step 6

Create product class inside models folder.

namespace RabitMqProductAPI.Models
{
    public class Product
    {
        public int ProductId { get; set; }
        public string ProductName { get; set; }
        public string ProductDescription { get; set; }
        public int ProductPrice { get; set; }
        public int ProductStock { get; set; }
    }
}

Step 7

Next, create a DbContextClass.cs class inside the data folder.

using Microsoft.EntityFrameworkCore;
using RabitMqProductAPI.Models;
namespace RabitMqProductAPI.Data
{
    public class DbContextClass : DbContext
    {
        protected readonly IConfiguration Configuration;
public DbContextClass(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
        }
public DbSet<Product> Products { get; set; }
    }
}

Step 8

Now create IProductService.cs and ProductService.cs classes inside Services folder.

using RabitMqProductAPI.Models;
namespace RabitMqProductAPI.Services
{
 public interface IProductService
 {
 public IEnumerable<Product> GetProductList();
 public Product GetProductById(int id);
 public Product AddProduct(Product product);
 public Product UpdateProduct(Product product);
 public bool DeleteProduct(int Id);
 }
}

Create ProductService.cs.

using RabitMqProductAPI.Data;
using RabitMqProductAPI.Models;
namespace RabitMqProductAPI.Services
{
 public class ProductService : IProductService
 {
 private readonly DbContextClass _dbContext;
 
 public ProductService(DbContextClass dbContext)
 {
 _dbContext = dbContext;
 }
public IEnumerable<Product> GetProductList()
 {
 return _dbContext.Products.ToList();
 }
 public Product GetProductById(int id)
 {
 return _dbContext.Products.Where(x => x.ProductId == id).FirstOrDefault();
 }
public Product AddProduct(Product product)
 {
 var result = _dbContext.Products.Add(product);
 _dbContext.SaveChanges();
 return result.Entity;
 }
public Product UpdateProduct(Product product)
 {
 var result = _dbContext.Products.Update(product);
 _dbContext.SaveChanges();
 return result.Entity;
 }
 public bool DeleteProduct(int Id)
 {
 var filteredData = _dbContext.Products.Where(x => x.ProductId == Id).FirstOrDefault();
 var result = _dbContext.Remove(filteredData);
 _dbContext.SaveChanges();
 return result != null ? true : false;
 }
 }
}

Step 9

Inside the RabbitMQ folder create classes IRabitMQProducer.cs and RabitMQProducer.cs for the message queue.

namespace RabitMqProductAPI.RabitMQ
{
 public interface IRabitMQProducer
 {
 public void SendProductMessage<T>(T message);
 }
}

Next, create the RabitMQProducer.cs class.

using Newtonsoft.Json;
using RabbitMQ.Client;
using System.Text;
namespace RabitMqProductAPI.RabitMQ
{
 public class RabitMQProducer : IRabitMQProducer
 {
 public void SendProductMessage<T>(T message)
 {
 //Here we specify the Rabbit MQ Server. we use rabbitmq docker image and use it
 var factory = new ConnectionFactory
 {
 HostName = "localhost"
 };
//Create the RabbitMQ connection using connection factory details as i mentioned above
 var connection = factory.CreateConnection();
//Here we create channel with session and model
 using var channel = connection.CreateModel();
//declare the queue after mentioning name and a few property related to that
 channel.QueueDeclare("product", exclusive: false);
//Serialize the message
 var json = JsonConvert.SerializeObject(message);
 var body = Encoding.UTF8.GetBytes(json);
//put the data on to the product queue
 channel.BasicPublish(exchange: "", routingKey: "product", body: body);
 }
 }
}

Step 10

After that, create a new ProductController.cs.

using Microsoft.AspNetCore.Mvc;
using RabitMqProductAPI.Models;
using RabitMqProductAPI.RabitMQ;
using RabitMqProductAPI.Services;
namespace RabitMqProductAPI.Controllers
{
 [Route("api/[controller]")]
 [ApiController]
 public class ProductController : ControllerBase
 {
 private readonly IProductService productService;
 private readonly IRabitMQProducer _rabitMQProducer;
public ProductController(IProductService _productService, IRabitMQProducer rabitMQProducer)
 {
 productService = _productService;
 _rabitMQProducer = rabitMQProducer;
 }
[HttpGet("productlist")]
 public IEnumerable<Product> ProductList()
 {
 var productList = productService.GetProductList();
 return productList;
}
 [HttpGet("getproductbyid")]
 public Product GetProductById(int Id)
 {
 return productService.GetProductById(Id);
 }
[HttpPost("addproduct")]
 public Product AddProduct(Product product)
 {
 var productData = productService.AddProduct(product);
//send the inserted product data to the queue and consumer will listening this data from queue
 _rabitMQProducer.SendProductMessage(productData);
return productData;
 }
[HttpPut("updateproduct")]
 public Product UpdateProduct(Product product)
 {
 return productService.UpdateProduct(product);
 }
[HttpDelete("deleteproduct")]
 public bool DeleteProduct(int Id)
 {
 return productService.DeleteProduct(Id);
 }
 }
}

Here, as you see, we will inject the IRabitMQProducer service inside the constructor and use it in the “Add Product” API endpoint to send data to the message queue. It inserts the product details inside the RabbitMQ queue, and afterwards, the consumer will receive that data – a continuous listening queue.

Step 11

Add a connection string inside the appsetting.json file.

{
 "Logging": {
 "LogLevel": {
 "Default": "Information",
 "Microsoft.AspNetCore": "Warning"
 }
 },
 "AllowedHosts": "*",
 "ConnectionStrings": {
 "DefaultConnection": "Data Source=DESKTOP-***;Initial Catalog=RabitMQDemo;User Id=**;Password=***@1;"
 }
}

Step 12

Next, register some services inside the Program.cs class.

using RabitMqProductAPI.Data;
using RabitMqProductAPI.RabitMQ;
using RabitMqProductAPI.Services;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddDbContext<DbContextClass>();
builder.Services.AddScoped<IRabitMQProducer, RabitMQProducer>();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
 app.UseSwagger();
 app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

Step 13

After executing in package manager console under main project – add and update migration using following entity framework command.

add-migration “first”update-database

Step 14

Install RabbitMQ Docker file using the following command. (Note: Docker Desktop is in running mode.)

docker pull rabbitmq:3-management

Next, create a container and start using the RabbitMQ Dockerfile we downloaded.

docker run — rm -it -p 15672:15672 -p 5672:5672 rabbitmq:3-management

step 15

Finally, run your application. You will see Swagger UI and API endpoint.

It’s all about the product Web API as the manufacturer. Let’s create a new console application as the consumer consumes the message that is sent by the producer.

step 1

Inside the same solution add a new console application.

step 2

Configure your new project.

step 3

Provide some additional information.

step 4

Install some NuGet packages.

Step 5

Add the following code inside Program.cs class.

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;
//Here we specify the Rabbit MQ Server. we use rabbitmq docker image and use it
var factory = new ConnectionFactory
{
 HostName = "localhost"
};
//Create the RabbitMQ connection using connection factory details as i mentioned above
var connection = factory.CreateConnection();
//Here we create channel with session and model
using var channel = connection.CreateModel();
//declare the queue after mentioning name and a few property related to that
channel.QueueDeclare("product", exclusive: false);
//Set Event object which listen message from chanel which is sent by producer
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, eventArgs) =>
{
 var body = eventArgs.Body.ToArray();
 var message = Encoding.UTF8.GetString(body);
Console.WriteLine($"Product message received: {message}");
};
//read the message
channel.BasicConsume(queue: "product", autoAck: true, consumer: consumer);
Console.ReadKey();

Step 6

Here is the final project structure.

Step 7

Go to Solution Properties and configure both the Producer and Consumer project as an initial project, as shown below.


Step 8

Open the following URL to open the RabbitMQ dashboard on the port we set when running Docker.

http://localhost:15672/

The URL login page opens.

Enter the default username “guest” and password, which is also “guest”. After this you will see the dashboard.

Open the Queue tab, in which you will see our product queue.


Step 9

Enter product details and execute API.

Step 10

When you execute the above API using Swagger, the message is sent to the queue. Inside the consumer’s console window, you can immediately see the product details they heard from the queue.


It’s all thanks to RabbitMQ. You can use it according to your needs and purposes.

conclusion

We discussed everything related to RabbitMQ, right from the introduction. We have installed a docker image and some functionalities related to producer and consumer using the product application.

Happy Learning!

Leave a Comment