See also this link:
RabbitMQ and Messaging Concepts (Udemy training): https://www.udemy.com/course/rabbitmq-and-messaging-concepts
It is a message broker (an intelligent message bus) that receives messages from producers and routes them to one or more consumers
It is open source, written in Erlang
It is simple and easy to use but powerful
Its features can be extended with plugins
Supports several messaging protocols
AMQP 0-9-1
STOMP 1.0 through 1.2
MQTT 3.1.1
AMQP 1.0
Available on Windows, Linux and Mac
There are also several ready to use Docker images on Docker hub
Install and run Docker Desktop on your machine, if you haven't already
You can download Docker from the following link: https://www.docker.com/products/docker-desktop
Start RabbitMQ in a Docker container by running the following command:
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management
The password is also guest
To create the project, you can use the dotnet new command to create a new Web API project
dotnet new webapi -o RabbitMQWebAPI
First, you will need to install the RabbitMQ.Client NuGet package in your project
dotnet add package RabbitMQ.Client
using RabbitMQ.Client;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
namespace RabbitMQWebAPI_producer
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
var factory = new ConnectionFactory()
{
HostName = "localhost",
UserName = "guest",
Password = "guest"
};
services.AddSingleton(factory.CreateConnection());
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "RabbitMQWebAPI", Version = "v1" });
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "RabbitMQWebAPI v1"));
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
For .NET 6 and above versions you can omit the Main and Startup
Program.cs
using RabbitMQ.Client;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
namespace RabbitMQWebAPI_producer
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
var factory = new ConnectionFactory()
{
HostName = "localhost",
UserName = "guest",
Password = "guest"
};
builder.Services.AddSingleton(factory.CreateConnection());
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "RabbitMQWebAPI", Version = "v1" });
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "RabbitMQWebAPI v1"));
}
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.Run();
}
namespace RabbitMQWebAPI_producer.Models
{
public class Value
{
public string Text { get; set; }
}
}
using RabbitMQ.Client;
using System.Text;
using Microsoft.AspNetCore.Mvc;
namespace RabbitMQWebAPI_producer.Controllers
{
[ApiController]
[Route("[controller]")]
public class ValuesController : ControllerBase
{
private readonly IConnection _connection;
public ValuesController(IConnection connection)
{
_connection = connection;
}
[HttpPost]
public void Post([FromBody] string value)
{
using var channel = _connection.CreateModel();
channel.QueueDeclare(queue: "hello",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
var body = Encoding.UTF8.GetBytes(value);
channel.BasicPublish(exchange: "",
routingKey: "hello",
basicProperties: null,
body: body);
}
}
}
We build and run the application in Visual Studio 2022 and we send a message to RabbitMQ executing the POST request
Inside RabbitMQ we navigate to queues and we verify we create a new queue called hello
We can get the messages inside the queue
https://github.com/luiscoco/RabbitMQ_FanoutDemo
The simplest exchange type, it sends all the messages it receives to all the queues that are bound to it.
It simply ignores the routing information and does not perform any filtering.
Like a postman that photocopies all the mails and puts one copy into each mailbox.
https://github.com/luiscoco/RabbitMQ_DirectDemo
Routes messages to the queues based on the "routing key" specified in binding definition.
In order to send a message to a queue, routing key on the message and the routing key of the bound queue must be exactly the same.
https://github.com/luiscoco/RabbitMQ_TopicDemo
Topic exchange will perform a wildcard match between the routing key and the routing pattern specified in the binding to publish a message to queue
https://github.com/luiscoco/RabbitMQ_HeadersDemo
In RabbitMQ, headers exchanges will use the message header attributes for routing
https://github.com/luiscoco/RabbitMQ_DefaultDemo
When a new queue is created on a RabbitMQ system, it is implicitly bound to a system exchange called "default exchange", with a routing key which is the same as the queue name
Default exchange has no name (empty string)
The type of the default exchange is "direct"
When sending a msessage, if exchange name is left empty, it is handled by the "default exchange"
https://github.com/luiscoco/RabbitMQ_ExchangeToExchangeDemo
The setup in your diagram includes two exchanges and two queues. Let's examine the message flow:
Exchange 1: Appears to be a direct exchange where messages are routed to the queues based on a matching routing key
Message 1 with routing key abc goes directly to Queue 1, because there's a binding between Exchange 1 and Queue 1 with that routing key
Message 2 with routing key xyz does not go to any queue from Exchange 1, because there's no binding with that key
Exchange 2: Also appears to be a direct exchange based on the routing
Message 3 with routing key 123 is not routed to any queue from Exchange 2 because there's no matching binding
Message 2 (presumably the same message that was sent to Exchange 1) and Message 4 both have routing key xyz and are routed to Queue 2, because there's a binding between Exchange 2 and Queue 2 with the routing key xyz
The dashed line from Message 2 indicates that it's being routed to Queue 2 via Exchange 2, not Exchange 1
https://github.com/luiscoco/RabbitMQ_AlternateDemo
The concept of an alternate exchange is used when you want to handle messages that cannot be routed to any queue
In this case, Exchange 1 is configured with an alternate exchange, Exchange 2
If a message is published to Exchange 1 with a routing key for which there is no matching queue binding, the message will be forwarded to the alternate exchange
There are two exchanges depicted in the diagram:
Exchange 1 (Direct): A direct exchange delivers messages to queues based on the message routing key
A direct exchange will route messages to the queue whose binding key exactly matches the routing key of the message
Exchange 2 (Fanout): A fanout exchange routes messages to all of the queues bound to it, without considering the routing key
It's like a broadcast; every queue gets a copy of the message
https://github.com/luiscoco/RabbitMQ_PushPullDemo
Push
Consumer application subscribes to the queue and waits for messages
If there is already a message on the queue, or when a new message arrives, it is automatically sent(pushed) to the consumer application
This is the suggested way of getting messages from a queue
Pull
Consumer application does not subscribe to the queue
But it constantly checks(pulls) the queue for new messages
If there is a message available on the queue, it is manually fetched(pulled) by the consumer application
Even though the pull mode is not recommended, it is the only solution when there is no live connection between message broker and consumer applications
Work queues are used to distribute tasks among multiple workers
Producers add tasks to a queue and these tasks are distributed to multiple worker applications
Pull or push models can be used to distribute tasks among the workers
https://github.com/luiscoco/RabbitMQ_PubSub
Sample:
https://github.com/luiscoco/RabbitMQ_RequestReply-QueueName-Demo
https://github.com/luiscoco/RabbitMQ_RequestReplyDemo
https://github.com/luiscoco/RabbitMQ_RequestReply-MatchingCoding-Demo
https://github.com/luiscoco/RabbitMQ_PriorityQueues
All the messages or tasks may not have the same urgency level
Some of them may be very urgent while others may be processed if there is no other message