A Beginner's Guide to MassTransit and RabbitMQ in ASP NET

MassTransit is a powerful .NET library for building distributed systems. Let's learn how to set up MassTransit using RabbitMQ transport from an ASP NET application. We will also learn the default queue topology MassTransit sets up on RabbitMQ transport.

Rahul Pulikkot Nath
Rahul Pulikkot Nath

Table of Contents

MassTransit is a powerful .NET library for building distributed systems.

It simplifies messaging and event-driven architecture, making creating scalable and resilient applications easy.

MassTransit supports various message transports, including RabbitMQ, Amazon SQS, Azure Service Bus, etc. It abstracts away the complexities of different transports, providing a unified API for sending and receiving messages across these platforms.

In this post, let’s learn how to use MassTransit from an ASP NET application.

We will learn how to set up MassTransit and start using it to publish and subscribe to messages. I'll also show you the default queue topology that MassTransit sets up when working with RabbitMQ transport.

I will use an AWS MQ-hosted instance of RabbitMQ as my transport.

This post is sponsored by AWS and is part of my RabbitMQ Series.

Installing and Setting Up MassTransit in ASP NET Core

To install MassTransit with RabbitMQ in an ASP.NET Core application, run the following commands to add the required NuGet packages:

dotnet add package MassTransit.AspNetCore
dotnet add package MassTransit.RabbitMQ

NuGet packages required for MassTransit with RabbitMQ transport.

This will install MassTransit and the RabbitMQ transport, which we will configure later in this blog post.

Publishing Messages Using MassTransit

I am using a default ASP NET Core Web API application for the demo.

Let's say we have an POST endpoint in it to add new weather forecast data to our application.

We must email a specific address whenever we add a weather forecast entry.

You could directly add the email sending logic to the application code that is responsible to add the new weather forecast.

However, this increases code coupling.

This makes it a perfect case to use message-driven architecture.

Any time new weather forecast data is added, let's raise WeaherDataAddedEvent, as shown below.

app.MapPost(
        "/weatherforecast", 
        async ([FromBody] WeatherForecast data, IBus bus) =>
    {
        Console.WriteLine($"New weather data added for {data.City} " +
                          $"on {data.Date} with Temperature {data.TemperatureC}");
        await bus.Publish(new WeatherDataAddedEvent()
        {
            City = data.City, TemperatureC = data.TemperatureC, DateTime = data.Date
        });
    })
    .WithName("PostWeatherForecast")
    .DisableAntiforgery()
    .WithOpenApi();

ASP NET Core POST endpoint, publishing an event to RabbitMQ using MassTransit.

The above code uses an instance ofIBus from the MassTransit library to publish the WeatherDataAddedEvent any time new weather forecast data is added.

IBus will be automatically injected into the controller once we set up MassTransit later in this post.

Consuming Messages Using MassTransit

To consume a message in MassTransit, Create a consumer class that implements IConsumer<T>, where T is the message type you're consuming.

The SendNewWeatherDataEmail class consumes the WeatherDataAddedEvent published from our POST endpoint.

public class SendNewWeatherDataEmail: IConsumer<WeatherDataAddedEvent>
{
    public Task Consume(ConsumeContext<WeatherDataAddedEvent> context)
    {
        Console.WriteLine(@$"Sending Email for City {context.Message.City} 
                          on {context.Message.DateTime} with 
                          {context.Message.TemperatureC}");
        return Task.CompletedTask;
    }
}

Message Consumer in MassTransit handling an event.

The consumer implements IConsumer<WeatherDataAddedEvent> interface.

Wiring MassTransit with RabbitMQ Transport

Now that we have set up our application code to publish messages and added a consumer for the message let's wire up MassTransit to use RabbitMQ as our transport mechanism.

If you are new to RabbitMQ, I highly recommend checking out the articles below to learn some basic concepts.

RabbitMQ - Rahul Nath
RabbitMQ is a powerful open-source message broker facilitating communication between systems or applications. Let’s learn how to get started using RabbitMQ on Amazon MQ from .NET application.

You can host a RabbitMQ instance in multiple ways. For this blog post, I use an AWS MQ-hosted instance of RabbitMQ.

Creating an AWS MQ hosted instance of RabbitMQ.

The following code in Program.csregisters MassTransit and sets up the RabbitMQ transport.

builder.Services.AddMassTransit(mt =>
{
    mt.AddConsumer<SendNewWeatherDataEmail>();
    
    var rabbitConfiguration = builder.Configuration
        .GetSection(nameof(RabbitConfiguration))
        .Get<RabbitConfiguration>();
    
    mt.UsingRabbitMq((context, cfg) =>
    {
        cfg.ConfigureEndpoints(context);
        cfg.Host(rabbitConfiguration.Host, host =>
        {
            host.Username(rabbitConfiguration.UserName);
            host.Password(rabbitConfiguration.Password);
        });
    });
});

Registering MassTransit and setting up RabbitMQ as transport at the startup of a ASP NET aplication.

The code first registers the SendNewWeatherDataEmail consumer class to MassTransit. This tells MassTransit about this consumer consuming the specific event.

We also set up to use RabbitMQ as the transport.

First, we retrieve the RabbitMQ configuration setting from appsettings.json and use that in the UsinRabbitMQ extension method to set up the RabbitMQ host details and credentials to connect.

MassTransit Default Queue Topology Convention

The ConfigureEndpoints the method sets up the channels and binding on RabbitMQ based on the default MassTransit convention.

Defauilt MassTransit Queue Topology for RabbitMQ.

MassTransit's default RabbitMQ topology setup:

Message Publication and Consumer Setup

MassTransit creates a fanout exchange named after the message type and class namespace (e.g., "hello_mass_transit:WeatherDataAddedEvent").

This is the primary point where publishers send messages.

For each consumer, MassTransit creates a unique queue named after the consumer (e.g., "SendNewWeatherDataEmail") and a fanout exchange, typically called the same (e.g., "SendNewWeatherDataEmail")

The different Exchanges setup by default based on the default MassTransit conventions.

Message Routing and Flow

MassTransit binds the message type exchange to each consumer's exchange and each exchange to its corresponding queue.

This is an example of Exchange to Exchange Bindings in RabbitMQ.

RabbitMQ Routing set up by MassTransit from the message publish exchange to each of the Consumers. This is an Exchange-to-Exchange binding.

The published message goes to the message type exchange and is forwarded to all bound consumer exchanges.

Each consumer exchange then routes the message to its specific queue. Consumers read messages from their dedicated queues.

RabbitMQ Queues set up by MassTransit for each consumer.

This setup allows multiple consumers to handle the same message type independently.

Publishers and consumers are decoupled, enhancing system flexibility.

This topology ensures efficient message distribution, independent consumer scaling, and a clear, predictable structure for complex messaging systems.

MassTransit handles the creation and configuration of all these RabbitMQ objects automatically based on convention.

The default topology set up by MassTransit can be modified through configuration.

We will explore this in a future post! 👋

RabbitMQAWS