An Opinionated Past-AWSSQS-Handling-Inspired RabbitMq Template Package For .NET WebApi π π
- Exactly the same as v1.0.4-dev, just more informative of the target .NET framework (net5.0).
- Publishing Messages & Adding Queues now requires you to use their respective dedicated class objects. Read more about them here.
- You can now define queues as
non-exclusive
,durable
, and/orautodelete
. Read more about them here.
- You can now define multiple queues under the same exchange. Read about them here.
- Create a new dotnet webapi project.
> dotnet new webapi -n SampleRabbitApi
> dotnet add package BraveFish.RabbitBattleGear --version 1.0.3-dev
- Create a rabbitBattleGearContext and Register For DI.
public static class RabbitContext
{
public static void AddRabbitContext(this IServiceCollection services)
{
var rabbitCtx = new RabbitBattleGearContextBuilder()
.SetHostName("localhost")
.SetPort(5672)
.SetUsername("guest")
.SetPassword("guest")
.SetMonoExchangeName("rabbit.ex")
.AddQueue(new QueueProps { QueueName = "rabbitLogger"})
.AddQueue(new QueueProps { QueueName = "myLogger" })
.Build();
services.AddSingleton(rabbitCtx);
}
}
- Create listeners for your MonoExchange. Inherit from abstract class
RabbitBattleListener
.
public class RabbitLogListener : RabbitBattleListener
{
private readonly ILogger<RabbitBattleListener> _logger;
public RabbitLogListener(RabbitBattleGearContext rabbitBattleGearContext,
ILogger<RabbitBattleListener> logger) : base(rabbitBattleGearContext)
{
_logger = logger;
}
// register which queue this listener is going to listen to.
protected override string GetQueueNameOfThisListener()
{
return "rabbitlogger";
}
// register your handlers per address (key).
protected override Dictionary<string, Action<string, RabbitBattleGearContext, BasicDeliverEventArgs>> GetBattleMessageHandlers()
{
var res = new Dictionary<string, Action<string, RabbitBattleGearContext, BasicDeliverEventArgs>>();
// Messages addressed to "LOG1" will have this handler
res.Add("LOG1", (message, rabbitContext, eventArgs) =>
{
// do something with the message
_logger.LogInformation("Log1: " + message);
// acknowledge
rabbitContext.AcknowledgeMessage(eventArgs);
});
// Messages addressed to "LOG2" will have this handler
res.Add("LOG2", (message, rabbitContext, eventArgs) =>
{
// do something with the message
_logger.LogInformation("Log2: " + message);
// acknowledge
rabbitContext.AcknowledgeMessage(eventArgs);
});
// Messages addressed to "LOG3" will have this handler
res.Add("LOG3", (message, rabbitContext, eventArgs) =>
{
var nowMinuteIsOdd = (DateTime.Now.Minute % 2 != 0);
if (nowMinuteIsOdd)
{
// do something with the message
_logger.LogInformation("Log3: " + message);
// acknowledge
rabbitContext.AcknowledgeMessage(eventArgs);
}
else
{
// do something with the message
_logger.LogError("Log3 not firing! will requeue");
// negative-acknowledged and re-published to queue
rabbitContext.IgnoreMessage(eventArgs);
}
});
// Messages that are not addressed in the dictionary here
// will be auto-acknowledged without any handlers and not
// republished.
return res;
}
}
public class MyLogListener : RabbitBattleListener
{
private readonly ILogger<MyLogListener> _logger;
public MyLogListener(RabbitBattleGearContext rabbitBattleGearContext,
ILogger<MyLogListener> logger) : base(rabbitBattleGearContext)
{
_logger = logger;
}
protected override string GetQueueNameOfThisListener()
{
return "mylogger";
}
protected override Dictionary<string, Action<string, RabbitBattleGearContext, BasicDeliverEventArgs>> GetBattleMessageHandlers()
{
var res = new Dictionary<string, Action<string, RabbitBattleGearContext, BasicDeliverEventArgs>>();
res.Add("MYLOG1", (message, rabbitContext, eventArgs) =>
{
_logger.LogInformation("MYLOG1 says: " + message);
rabbitContext.AcknowledgeMessage(eventArgs);
});
res.Add("MYLOG2", (message, rabbitContext, eventArgs) =>
{
_logger.LogInformation("MYLOG2 says: " + message);
rabbitContext.AcknowledgeMessage(eventArgs);
});
return res;
}
}
- Create controller to publish a message. Use the
RabbitBattleGearContext
to publish messages.
[ApiController]
[Route("[controller]")]
public class RabbitLoggerController : ControllerBase
{
private readonly RabbitBattleGearContext _rabbitBattleGearContext;
public RabbitLoggerController(RabbitBattleGearContext rabbitBattleGearContext)
{
_rabbitBattleGearContext = rabbitBattleGearContext;
}
[HttpGet("publishMessageToRabbitLogger/{id}")]
public string PublishMessageToRabbitLogger(string id)
{
_rabbitBattleGearContext.PublishMessage(new PublishMessageRequest
{
QueueName = "rabbitLogger",
Address = $"LOG{id}",
Message = "hello there!"
});
return "published";
}
[HttpGet("publishMessageToMyLogger/{id}")]
public string PublishMessageToMyLogger(string id)
{
_rabbitBattleGearContext.PublishMessage(new PublishMessageRequest
{
QueueName = "myLogger",
Address = $"MYLOG{id}",
Message = $"Message received at myLogger LOG{id}"
});
return "published";
}
}
- Register the DI for RabbitBattleGearContext & the Listener
public void ConfigureServices(IServiceCollection services)
{
// other services
services.AddRabbitContext();
services.AddHostedService<RabbitLogListener>();
services.AddHostedService<MyLogListener>();
}
By default, this package assumes the queue is non-exclusive
. It means that other applications can define an existing queue and publish messages to them. If your queue is set to exclusive: true
, any application that declares the same queue will not be able to start/run.
// Default (Non-Exclusive)
.AddQueue(new QueueProps { QueueName = "rabbitLogger"})
// Exclusive
.AddQueue(new QueueProps { QueueName = "rabbitLogger", Exclusive = false})
Autodelete is by default set to false
in this package. This will prevent queues from being taken down by unexpected application down-time(s). You can override this behaviour like so:
// Default (AutoDelete: false)
.AddQueue(new QueueProps { QueueName = "myLogger" })
// AutoDelete: true
.AddQueue(new QueueProps { QueueName = "rabbitLogger", AutoDelete = true})
This package also assumes by default that the queue is durable
and publishes persistent
messages. This is for unpredictable restarts of the RabbitMQ instance itself as mentioned here. You could however still set durability
and persistence
manually to fulfill your needs.
// Queue Durability: setup in the RabbitBattleGearContextBuilder
// Default (Non-Exclusive)
.AddQueue(new QueueProps { QueueName = "myLogger" })
// Durable
.AddQueue(new QueueProps { QueueName = "rabbitLogger", Durable = false})
// Message Persistence: choose publishing method
// Default: Persistent => True
_rabbitBattleGearContext.PublishMessage(new PublishMessageRequest
{
QueueName = "myLogger",
Address = $"MYLOG{id}",
Message = $"Message received at myLogger LOG{id}"
});
// Persistent => False
_rabbitBattleGearContext.PublishMessage(new PublishMessageRequest
{
QueueName = "myLogger",
Address = $"MYLOG{id}",
Message = $"Message received at myLogger LOG{id}",
Persistent = false
});