Skip to main content
.NET

How to use MassTransit in .NET Core with RabbitMQ

β€” Christian Schou

MassTransit... Are you looking for an open-source distributed application framework for .NET that simplifies the development of message-based applications? MassTransit provides a simple, yet powerful, way to build distributed systems using message-based architectures such as event-driven, publish/subscribe, and request/response patterns. Sounds awesome right? πŸ‘Œ

In this tutorial, I will show you how to use MassTransit with .NET in an ASP.NET Core Web API. The Web API will be based on a free template I have prepared for you. The link will be shown below in the requirements. You are also more than welcome to follow along with your own project. 😊

By the end of this tutorial about MassTransit, you can set up and configure a producer and consumer message service for RabbitMQ. We are also going to have a look at some advanced use cases for RabbitMQ and cover some concepts that are crucial to working with distributed solutions. If you are ready, then let's get started. πŸ˜€

Requirements πŸ‘¨β€πŸ”§

Before you move on to the fun part, please read the list below of what you need to continue/follow along.

  • Visual Studio Code, Visual Studio IDE, or any other C# text editor.
  • Knowledge about C# and the .NET Framework.
  • Knowledge about RabbitMQ.
  • A project to implement the producer and consumer in. See the link below to my CRUD API template. It's available for free, and you are welcome to grab a copy. ✌️
  • Docker (to host and run RabbitMQ)
GitHub - Tech-With-Christian/Dotnet-CRUD-Api: This is a simple Demo CRUD API for use in my tutorials. It contains a simple implementation of a CRUD service for products.
This is a simple Demo CRUD API for use in my tutorials. It contains a simple implementation of a CRUD service for products. - GitHub - Tech-With-Christian/Dotnet-CRUD-Api: This is a simple Demo CRU…

An introduction to MassTransit

MassTransit is an open-source distributed application framework for .NET that simplifies the development of message-based applications. It provides a simple, yet powerful, way to build distributed systems using message-based architectures such as event-driven, publish/subscribe, and request/response patterns.

MassTransit Β· MassTransit
An open-source distributed application framework for .NET

With MassTransit, developers can build robust, scalable, and fault-tolerant distributed applications that can communicate across different platforms and languages. Sweet! πŸ’ͺ It supports various messaging transports and protocols, including RabbitMQ, Azure Service Bus, Amazon Simple Queue Service (SQS), and Apache Kafka. You can easily switch between the platforms! 😊

MassTransit also provides features such as message serialization, message routing, message retry policies, and message monitoring, which help developers to build highly resilient and reliable distributed systems. What's not to like here?! πŸ˜…

In summary, MassTransit is a powerful and flexible messaging framework that helps .NET developers build scalable and resilient distributed systems using a message-based architecture. Exactly what we are looking for!

Why should we use MassTransit?

After reading the official documentation for MassTransit and having a basic knowledge of how RabbitMQ and Azure Service Bus works, it came clear to my mind that there are several reasons why you as a developer would go with a tool like MassTransit. Below are my top 5 reasons for choosing MassTransit.

  1. Simplified Development - MassTransit provides a simplified programming model for building message-based applications. It takes care of low-level details like message serialization, routing, and transport, allowing you to focus on writing business logic.
  2. Language and Platform Interoperability - MassTransit supports a wide range of messaging transports and protocols, making it easy to build distributed systems that communicate across different platforms and languages. This means that you can use different programming languages and platforms of your choice, while still communicating with other systems seamlessly. Imagine having teams writing in their own preferred programming languages where each service can communicate without knowing the background implementation - That's powerful!
  3. Resilient and Fault-Tolerant - MassTransit includes features like message retries and circuit breakers, which make it easier to build highly resilient and fault-tolerant distributed systems. These features help ensure that messages are delivered reliably, even in the face of network failures and other issues.
  4. Scalability - MassTransit is designed to support distributed systems that can scale easily. It provides features like load balancing and partitioning, which make it easy to handle large volumes of messages and distribute them across multiple nodes.
  5. Open-Source and Community-Driven - MassTransit is an open-source project with an active community of contributors. This means that you can benefit from a wide range of features, bug fixes, and enhancements contributed by other members of the community.

A quick introduction to RabbitMQ

Before we move to the part you are actually here for - the implementation, I think it's very important that you have a basic understanding of what RabbitMQ is and how queues and exchanges work.

Messaging that just works β€” RabbitMQ

RabbitMQ is an open-source message broker 🚌 software that provides a messaging system to send and receive messages between different applications and services. It is a robust and scalable messaging platform that is widely used in distributed systems.

RabbitMQ works by implementing the Advanced Message Queuing Protocol (AMQP), a standard messaging protocol for message-oriented middleware. RabbitMQ supports various messaging patterns, such as publish/subscribe, request/response, and work queues. It provides message queues, exchanges, and bindings, which enable different applications and services to exchange messages in a distributed environment.

RabbitMQ can be used with a wide range of programming languages and platforms, including Java, .NET, Python, Ruby, and many more. It is also compatible with various messaging protocols, such as AMQP, STOMP, MQTT, and HTTP. ✌️

Why should you use RabbitMQ?

RabbitMQ is a reliable and scalable messaging platform for distributed systems. Below are some key takeaway features of RabbitMQ.

  • High Availability - RabbitMQ supports clustering, which allows multiple nodes to be connected and provides high availability and fault tolerance.
  • Routing - RabbitMQ supports different routing algorithms and message exchange types, which enable messages to be routed to the correct destination.
  • Message Durability - RabbitMQ provides message durability by persisting messages to disk, which ensures that messages are not lost even in the event of a system failure.
  • Security - RabbitMQ provides various security features, such as SSL/TLS encryption, access control, and user authentication.

RabbitMQ Queues

A RabbitMQ message queue is a buffer that stores messages until they can be processed by the consuming application or service. A message queue acts as a temporary storage location for messages that are produced by a producer application and consumed by one or more consumer applications.

Each message in a queue has a unique identifier and is stored in memory or on disk until it is consumed by a consumer or until it reaches its time-to-live (TTL). RabbitMQ queues can be configured to support different types of message delivery, such as round-robin, priority-based, or topic-based routing.

Below is an image showing at a high level how a message queue works. You got one or more producers generating messages and publishing them to a RabbitMQ queue. One or more consumers can then consume the messages in the queue.

rabbitmq, message queue, rabbitmq message queue, rabbitmq messages, consumer/producer
RabbitMQ Message Queue Concept

You can think of a queue as a large buffer that can hold all your messages with data for the consumers to handle.

RabbitMQ Exchanges

We already know what a queue is, let's go a little deeper and look at exchanges. An exchange is a component of the message broker that receives messages from producers and routes them to message queues based on specific routing rules (genius btw!). Exchanges are responsible for receiving messages and forwarding them to one or more queues based on the message type and routing keys.

AMQP 0-9-1 Model Explained β€” RabbitMQ

So how does that work? Glad you asked! 😊 When a producer sends a message to RabbitMQ, it is sent to an exchange, which then routes the message to one or more message queues. The exchange determines which queue(s) the message should be sent to based on the message type and the routing key.

By default MassTransit uses Fanout as the routing algorithm, hence I will focus on that one in this tutorial. For your reference, I have included a list below of the different types of exchanges RabbitMQ supports when talking about the routing of messages.

  1. Direct Exchange - Direct exchanges route messages to a queue based on the exact match between the routing key and the binding key.
  2. Fanout Exchange - Fanout exchanges route messages to all the queues that are bound to them, regardless of the routing key. (this is the one we will be working with in this tutorial)
  3. Topic Exchange - Topic exchanges route messages based on pattern matching between the routing key and the binding key. The routing key can contain wildcards, allowing for more flexible routing.
  4. Headers Exchange - Headers exchanges route messages based on the headers of the message, which can be any key-value pair.

Below is an illustration of how the Fanout Exchange works in RabbitMQ.

fanout, exchange, message queue, rabbitmq, fanout exchange, producer/consumer
Fanout Exchange in RabbitMQ

As you can see the exchange (fanout) is responsible for receiving messages from producers and routing them to the appropriate message queues based on routing rules. To accomplish this we have to create a binding, to make sure that the messages go to the correct queue.


Implementation of MassTransit in .NET Core

Okay - now for the part you have been waiting for. Btw. if you have read all the introductions above, kudos! πŸ‘ I know it was a long wall of text, but it's crucial that you have that knowledge in order to create a distributed application and understand what is going on under the hood. 🧠

Deploy RabbitMQ using Docker πŸ‹

The first thing we have to do is make sure that we got a running instance of RabbitMQ πŸ‡. For this tutorial, I will be using Docker to start up and run RabbitMQ. You can open up PowerShell or your Terminal and enter the following command below. Works on both Windows and Linux.

docker run -d --hostname twc-rabbitmq-server --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.11-management

What happened when you hit ENTER?

docker, pull image, pull docker image
Pull RabbitMQ Image from Docker
  • We pulled the latest (as of writing) available management image of RabbitMQ from Docker.
  • The hostname is configured to twc-rabbitmq-server. You are welcome to change this to anything you like.
  • The name is set to rabbitmq.
  • Ports 5672 and 15672 has been published for the container and mapped to the same port on the inside. Port 5672 is the port our application will use to send/publish messages and 15672 is the administration panel we can open up in the browser.

Now open up the browser and head to localhost:15672 to see RabbitMQ. You can sign in with the default login: guest/guest. I will discuss this web panel later on in the article. For now, all you should see is like in the image below. (click to enlarge)

rabbitmq, rabbitmq management
RabbitMQ Management Portal

If you got Docker Desktop running, you can easily check the logs if you encounter any errors.

rabbitmq, rabbitmq logs, docker desktop, docker logs
Docker Desktop RabbitMQ Logs

Prepare the project

I have created a new project based on my .NET CRUD API template. It is a simple CRUD API for use in my tutorials. It contains a simple implementation of a CRUD service for products, that can easily be extended. This time we will extend it with a message service using MassTransit.

You can find the final MassTransit demo project here at the link below.

GitHub - Tech-With-Christian/MassTransitDemo: This repository demonstrates how to implement a Message Queue service using MassTransit with RabbitMQ.
This repository demonstrates how to implement a Message Queue service using MassTransit with RabbitMQ. - GitHub - Tech-With-Christian/MassTransitDemo: This repository demonstrates how to implement…

Alright - let's move on. We now got a project with clean architecture and would like to implement the message service.


Create a new MessageBus class library

If you plan to expand the solution with multiple projects or got other services consuming messages from the same RabbitMQ instance, it's crucial that the namespace is identical in all the projects.

First, you have to create a new folder named Shared in the src folder. Inside the Shared folder create a new Class Library project named MessageBus. Now create two new folders inside the MessageBus project named Common and Messages. You should now have a structure like the one below.

Visual Studio Solution Explorer, messagebus, messages
Visual Studio Solution Explorer

Inside the Messages folder create a new class named IntegrationBaseEvent. This class should be inherited by all event types in order to provide a consistent set of properties for the ID and Created date. Add the following code inside the class:

namespace MessageBus.Messages
{
    public class IntegrationBaseEvent
    {
        public IntegrationBaseEvent()
        {
            Id = Guid.NewGuid();
            CreationDate = DateTime.Now;
        }

        public IntegrationBaseEvent(Guid id, DateTime creationDate)
        {
            Id = id;
            CreationDate = creationDate;
        }

        public Guid Id { get; private set; }
        public DateTime CreationDate { get; private set; }
    }
}

Inside the Common folder add a new class named EventBusContants. This is used to define constants for the name of the queues we will publish new messages to and consume from. Add the following code for now in the class.

namespace MessageBus.Common
{
    public class EventBusConstants
    {
        public const string ProductCreatedQueue = "product-created-queue";
    }
}

As you might have guessed, we will be creating a new event each time a new product is created in the API.

So why is it so important to define these classes in a shared project for the message bus? Well... When we are dealing with messages, we have to define a .NET class or interface (the choice is yours). MassTransit will automatically include the namespace of the event class we are using for generating the event in the message contract, hence we should use a shared library to make sure it's the same all time.

Add a new Event Class for when a Product is Created