Asynchronous Communication Between Microservices Using .NET Core API, RabbitMQ, and Docker

Introduction

The Asynchronous communication between microservices using .NET Core API, RabbitMQ, and Docker is a common pattern for building scalable and decoupled systems. Here’s a step-by-step guide with a code example:

Step 1: Create Microservices

For the sake of this example, let’s create two microservices: OrderService and EmailService.

Step 2: Set Up RabbitMQ

  1. Install the RabbitMQ server or use a Docker container:
   docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management
  1. Access the RabbitMQ management dashboard at http://localhost:15672. Use guest as both username and password (this is for development purposes; in production, you’d configure secure credentials).

Step 3: Create the OrderService

  1. Create a new .NET Core API project named OrderService.
  2. Install the NuGet package RabbitMQ.Client:
   dotnet add package RabbitMQ.Client
  1. Configure the Startup.cs to set up RabbitMQ:
using RabbitMQ.Client;

// ...

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddSingleton(factory => new ConnectionFactory
    {
        HostName = "localhost", // RabbitMQ server hostname
        Port = 5672,            // Default RabbitMQ port
        UserName = "guest",
        Password = "guest"
    });
    services.AddSingleton<IConnection>(sp => sp.GetRequiredService<ConnectionFactory>().CreateConnection());
    services.AddSingleton(sp => new OrderMessageProducer(sp.GetRequiredService<IConnection>()));
}
  1. Create a OrderMessageProducer class to send messages:
using RabbitMQ.Client;
using System.Text;

public class OrderMessageProducer
{
    private readonly IConnection _connection;

    public OrderMessageProducer(IConnection connection)
    {
        _connection = connection;
    }

    public void SendOrderMessage(string message)
    {
        using (var channel = _connection.CreateModel())
        {
            channel.QueueDeclare(queue: "order_queue",
                                 durable: false,
                                 exclusive: false,
                                 autoDelete: false,
                                 arguments: null);

            var body = Encoding.UTF8.GetBytes(message);

            channel.BasicPublish(exchange: "",
                                 routingKey: "order_queue",
                                 basicProperties: null,
                                 body: body);
        }
    }
}
  1. In your controller, inject OrderMessageProducer and send a message:
[ApiController]
[Route("api/[controller]")]
public class OrderController : ControllerBase
{
    private readonly OrderMessageProducer _messageProducer;

    public OrderController(OrderMessageProducer messageProducer)
    {
        _messageProducer = messageProducer;
    }

    [HttpPost]
    public IActionResult Post([FromBody] OrderDto order)
    {
        // ... save order ...

        // Send a message to RabbitMQ
        _messageProducer.SendOrderMessage($"New order: {order.OrderId}");

        return Ok();
    }
}

Step 4: Create the EmailService

  1. Create a new .NET Core API project named EmailService.
  2. Configure the Startup.cs to set up RabbitMQ as before.
  3. Create a EmailMessageConsumer class to consume messages:
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

public class EmailMessageConsumer
{
    private readonly IConnection _connection;

    public EmailMessageConsumer(IConnection connection)
    {
        _connection = connection;
    }

    public void StartListening()
    {
        using (var channel = _connection.CreateModel())
        {
            channel.QueueDeclare(queue: "order_queue",
                                 durable: false,
                                 exclusive: false,
                                 autoDelete: false,
                                 arguments: null);

            var consumer = new EventingBasicConsumer(channel);
            consumer.Received += (model, ea) =>
            {
                var body = ea.Body;
                var message = Encoding.UTF8.GetString(body);
                Console.WriteLine($"Received order message: {message}");
            };

            channel.BasicConsume(queue: "order_queue",
                                 autoAck: true,
                                 consumer: consumer);
        }
    }
}
  1. In the Startup.cs, add the EmailMessageConsumer to the service container and start listening:
public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddSingleton(sp => new EmailMessageConsumer(sp.GetRequiredService<IConnection>()));
}
  1. In the Program.cs, start the listener when the application starts:
public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();

        // Start the RabbitMQ message listener
        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;
            var emailMessageConsumer = services.GetRequiredService<EmailMessageConsumer>();
            emailMessageConsumer.StartListening();
        }

        host.Run();
    }

    // ...
}

Step 5: Run Microservices in Docker Containers

  1. Build Docker images for each microservice:
   docker build -t orderservice -f path/to/OrderService/Dockerfile .
   docker build -t emailservice -f path/to/EmailService/Dockerfile .
  1. Run Docker containers for each microservice:
   docker run -d --name orderservice orderservice
   docker run -d --name emailservice emailservice

Step 6: Test the Communication

  1. Send a POST request to the OrderService API to simulate creating a new order.
  2. Check the console output of the EmailService container to see the received message.

This example demonstrates a basic setup of asynchronous communication between microservices using RabbitMQ and Docker. In a real-world scenario, you would handle more complex scenarios, error handling, scalability, and ensure data integrity.

Please note that this example provides a foundation for understanding the communication pattern. Depending on your specific use case, you may need to consider additional aspects, such as message durability,

Leave a Reply

Your email address will not be published. Required fields are marked *