Use MassTransit + RabbitMQ with .Net Core 3.1

Feyyaz Acet
11 min readFeb 10, 2021

Hi guys, this article has also a Turkish version. İf you want to read the Turkish version you can find here.

You know, In recent years microservices became high popular approach/architecture while you develop projects. And one of the important things is communication between them. So In this article, I will try the explain my experience with MassTransit and how I handled problems I had while I was developing async communication between microservices.

RabbitMq: It is a message broker that helps us make async communication between microservice or two services. In other words, it is a message-queued that enables messaging for common platforms.

Masstransit: It is a free, open-source, lightweight message bus used to create distributed applications using .Net technologies. We can also say that it is an abstraction structure between the message brokers we use for asynchronous messaging and our application. Although it is referred to as an ESB (enterprise service bus) in many places (including Wikipedia), they always state that they are not ESB in a humble manner. MassTransit supports Azure Service Bus, ActiveMQ and Amazon SQS / SNS message-queues along with
Rabbitmq. You do not need to write any extra code in the transformation between them.

So where does Masstransit come into play? Why do we need it?

I assume you have decided to apply microservice architecture and separate the dependencies of your application by bounded context or make some work asynchronous in your application. First, you must choose a message broker. (In our example, it will be Rabbitmq.) When you start using a message broker, you will face many challenges over time. Error management, retry, Circuit broker, transaction. At this point, our big brothers Chris Patterson and Dru Sellers met at alt.net in 2007 and had seen that they faced the same problems. These brothers started this project both themselves and us to not struggle so much.

What are the Masstransit Advantages ?

Opensource and free
Easy to use
It contains many microservice-message patterns (such as retry, circuit breaker, outbox).
Exception Handling comes as built-in.
Distributed transaction (Saga, event-driven state machines, routing-slip)
You can test easy
Monitoring

Things you need to know…

Messages

Messages in Masstransit use .Net System Types. It can be the Message Type class or interface. MassTransit recommends read-only properties and types without methods. Our brothers who developed this framework recommend the interface as the most suitable type based on their previous experiences. They even go a little further and state that many programmers define message contract as a base class as a common error and dispatch the base class on the consumer side, and then this process is painful.

There are 2 ways of transmitting the message in MassTransit. Publish and Send. If you will send an event, you must use Publish, and if it is command, you must use Send. When sending commands, we specify the endpoint (which queue to go to). However, we do not do this for Event. Publish follows the Publish-Subscribe pattern. In other words, when we publish the event, a message is sent to all queues that subscribe to our event.

Note: If you want to get information about the Publish-Subcribe pattern, let’s go here.

Events

Events should be published rather than forwarded(send to queue)directly to the enpoint. It means something happened. Events should be expressed in a noun-verb (past tense) sequence, indicating that something happened.

Sample Events:

  • CustomerAddressUpdated
  • CustomerAccountUpgraded
  • OrderSubmitted,
  • OrderAccepted,
  • OrderRejected,
  • OrderShipped

Command

It is used when something needs to be done. It is recommended to use imperative mode.

Sample Commands

  • RegisterOrder,
  • UpdateCustomer
  • ChangeOrderState

In order not to go beyond the subject, I will not give an additional explanation about the terms. If you want to get more information about rabbitmq and terms, let’s go to my article about RabbitMq. You can find here

I would like to talk about our sample application without too much ado. I will also explain the structure used during development. I will give a short example from the process of the company that I have been a consultant. First of all, I will create a .net core web API project and receive customer demand from there. I will then queue this demand to save it. After that I will send a queue for this demand. On the consumer side, I will register this demand and publish event to send emails to the Customer Representative and integrate the third-party application. Finally, I will create 2 consumers (ThirPartyService and NotificationService) to handle an event.

Let’s start..?

I will develop the application on the windows platform with visual studio community edition with .net core 3.1. Additionally rabbitmq must be installed. I’ll get it up with docker. You can upload it yourself or follow the steps with me.

Let’s start by getting rabbitmq up with the docker. Running the following command will download the rabbitmq image in your local computer and launch it as a container.

docker run -d --hostname my-rabbit --name myrabbit -e RABBITMQ_DEFAULT_USER=guest -e RABBITMQ_DEFAULT_PASS=123456 -p 5672:5672 -p 15672:15672 rabbitmq:3-management

There is no problem, If the following screen opens when you try to enter http: //localhost: 5672 from the browser. I will not go into detailed information about Rabbitmq management panel. Let’s get it here if you’re wondering.

To work on a separate virtualhost in the project, I create a virtualhost named demand as follows. You can think of the virtual host as a separate domain/url that we will use for the application we will make.

Firstly, we create a blank solution called DemandMenagement. Subsequently, DemandMenagement.Api (Web Api) to receive the demand, DemandManagement.Registration (console) to save the demand to the database asynchronously, DemandManagement.Thirdparty.Service (console) to integrate the demand into a thirdparty project and DemandManagement.Notification (console )to report the demand to customer service. And finaly, create DemandManagement.MessageContracts (library) project to use common type (message contract). The final picture of the solution should be below.

I begin writing by define the interfaces that we will use in common. Inside the DemandManagement.MessageContracts project, I created the IRegisterDemandCommand interface to record the request and the IRegisteredDemandEvent interface to inform the consumers listening to the recorded demand. Along with these, I defined the BusConfigurator class as static to manage the bus, and RabbitMqConsts class for managing rabbitmq access and queue information. Now it’s time to install Masstrainsit.RabbitMQ package from Nuget. (We are installing this package for all our projects.) Likewise, all our projects will add the DemandManagement.MessageContracts project as a reference.

BusConfigurator Class

The final version of the BusConfigurator class is as it follows.

The final version of the RabbitMqConsts class is as it follows.

It’s time to write the api side. I created a folder named model in the root directory. And I created RegisterDemand class to post the demand. First of all, I add a controller called DemandControler. And I just configure the controller to post demand.

The final version of DemandController is as it follows

The final version of the RegisterDemandModel class is as it follows.

I use the BusConfigurator class to pass the request in the post action to the queue. However, I create an endpoint to forward the demand to the queue and send the message to the queue through this endpoint.

Post Action is as follows.

Then we move on to DemandManagement.Registration project. We add the DemandManagement.MessageContract project to our project as a reference and add the MassTransit.RabbitMQ package from Nuget.

In program.cs main method I am getting up the servicebus and subscribe to the registerdemand.service queue to listening. As soon as a record arrives in the relevant queue, it will be pushed here. Finaly create a class called RegisterDemandCommandConsumer to process the message.

Now, with the postman we will send our first demand to the queue and consume the demand on the DemandManagement.Registration side.

Before start, we right-click the properties on the solution, mark them as multiple startup projects from the Startup Project tab, and select the API and registration projects as a start. Then we get our projects up with F5.

Before start, we right-click the properties on the solution, mark them as multiple startup projects from the Startup Project tab, and select the API and registration projects as a start. Then we get our projects up with F5.

We open the Postman and post our demand with Postman as follows.

Postman: It is an application that we can easily call our Restful services.

The result of the post is as it follows.

Registiration consumer console. Information processed demand.

A queue named registerdemand.service has been created under the demand virtualhost on Rabbitmq side. MassTransit automatically created this for us. Likewise, it automatically created several exchanges to manage messages. Masstransit uses the Fanout exchange type by default. If anyone wants to get detailed information about exchanges, let me take him/her here.

In the second phase of our project, we will pubish an event with the Id generated for the demand processed on the registerdemand.service side. Again, we will processing the same event separately on third-party and notification side that subscribe to this event. First of all, we will create an event on the RegisterDemandCommandConsumer.cs side and send it to queues.

RegisterDemandCommandConsumer.cs is as it follows.

Now It’s time to subscribe to the IRegisteredDemand event on DemandManagement.Thirdparty.Service and DemandManagement.Notification. Actually, it is very simple to do this again. Like in the Registration project, it will be enough to get the Service Bus up and listen to the relevant queue. We will listen tothirdparty.service for the thirdparty.Service and notification.service queue for the notification.

For these, we will add the MessageContract project to both projects as a reference and installing the MassTransit.RabbitMQ package. Likewise, we create our consumer class in both projects to use the message that comes with the event. The final version of the pragram.cs and consumer classes of the two projects is as follows.

For DemandManagement.Notification

Program.cs main
DemandRegisteredEventConsumer

For DemandManagement.ThirdParty

Program.cs main
DemandRegisteredEventConsumer

Yes we finished coding. Now we can take the test. We set all projects to stand up, except MessageContract.

Yes, we finished coding. Now we can test now. We set all projects to stand up, except MessageContract.

Now time to run project. If you come across a screen like the below, you are doing quite right 😊

As in the previous Postman screen, we post the demand again. And the result is as follows.

Probably you aware it. We specified a queue when sending the DemandRegisterCommand, but why did not specify it when sending DemandRegisteredEvent, and how it the message gone to queues subscribed to this event? Here is the answer to why MassTransit uses Fanout exchange by default. You can find the details in my previous article.

So far so good. We have completed the happy path. MassTransit not only manages the communication processes such as channel and exchange for us but also provides a lot of convenience on the consumer side.

Let’s imagine the database can be under pressure sometimes. In this case, if we continue to request db, probably we come across deadlock. We don’t want do this. MassTransit includes CircutBreaker middleware built-in. All we do is activate and implement it according to our needs. The final version of the program.cs main method in DemandManagement.Registration is as follows.

Note: You can view Martin Fowler’s article for detailed information about Circuit Breaker pattern.

We configured that it should wait 5 minutes with ResetInterval if we get errors 15% or 10 times errors of demand. With TrackingPeriod we configured after ResetInterval time observe the system 1 more minute. During this time if come across the error wait up to ResetInterval time without wait TrackingPeriod limits.

We suppose , In DemandManagement.Notification service disconnected sometimes. Normally Masstransit create [came across queue].error and adds a message here that came across an error. If we know that the error is temporary so, we can use the UseMessageRetry extension method and confiure it according to our needs.

Note: You can search as retry pattern and get detailed information.

The final version of the program.cs main method in demand
follows.

The final version of the program.cs main method in DemandManagement.Notification is as follows.

In this way, it will try 5 times for to do the relevant operation(message). If the error persists, it will send the message to the error queue and continue to the next. Of course, the MassTransit favor don’t stop there.

We talked about integration in the DemandManagement.Thirdpary.Service project. In many integrations, there may be certain request limits within a certain time. Finally, I want to talk about RateLimit middleware. With RateLimit middlewere, we can give the number of messages to be processed within a certain period of time. I am editing the service assuming that we can make 1000 requests within 1 minute.

The final version of the program.cs main method in DemandManagement.Thirdpary.Service after development is as follows.

DemandManagement.Thirdpary.Service program.cs main

Conclusion

If somehow our path crosses with message brokers and we are working in the .Net Environment, MassTransit is a good ESB (enterprise service bus) alternative in terms of ease of use and managing the problems we will meet.

You can find the codes of the project here.

I hope it was useful. See you in the next article, stay well.

--

--