Introduce RabbitMQ consumer pool for parallel processing
Enhanced RabbitMQ email processing by introducing a `SendingEmailConsumerPool` to enable the competing consumers pattern. Each consumer operates on its own channel, improving scalability and thread safety. - Added `SendingEmailConsumerPool` to manage multiple consumers. - Updated `DependencyInjection` to register the consumer pool. - Refactored `SendingEmailConsumer` for better logging and error handling. - Updated `AsyncInitWorker` to initialize the consumer pool. - Added `ConsumerConcurrency` to RabbitMQ configuration. - Improved error handling in `LimilabsEmailService` with detailed SMTP error messages.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
using DigitalData.MessagingService.Application.Common.Interfaces;
|
||||
using DigitalData.MessagingService.RabbitMQ;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace DigitalData.MessagingService.Infrastructure.Queue;
|
||||
|
||||
/// <summary>
|
||||
/// Manages a pool of <see cref="SendingEmailConsumer"/> instances that compete for messages
|
||||
/// on the same RabbitMQ queue (competing consumers pattern).
|
||||
/// Each consumer owns a dedicated channel, so they process messages fully in parallel
|
||||
/// without any shared locking or synchronization primitives.
|
||||
/// </summary>
|
||||
public sealed class SendingEmailConsumerPool : IAsyncDisposable
|
||||
{
|
||||
private readonly List<SendingEmailConsumer> _consumers;
|
||||
private readonly ILogger<SendingEmailConsumerPool>? _logger;
|
||||
private readonly int _concurrency;
|
||||
|
||||
public SendingEmailConsumerPool(
|
||||
IOptions<RabbitMqConfiguration> config,
|
||||
IEmailService emailService,
|
||||
RabbitMqConnectionFactory cnnFactory,
|
||||
ILogger<SendingEmailConsumerPool>? logger = null,
|
||||
ILogger<SendingEmailConsumer>? consumerLogger = null)
|
||||
{
|
||||
_logger = logger;
|
||||
_concurrency = config.Value.ConsumerConcurrency;
|
||||
|
||||
_consumers = [.. Enumerable
|
||||
.Range(0, _concurrency)
|
||||
.Select(_ => new SendingEmailConsumer(config.Value.QueueName, emailService, cnnFactory, consumerLogger))];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts all consumers in parallel. Each consumer opens its own channel and begins listening.
|
||||
/// </summary>
|
||||
public async Task InitAsync()
|
||||
{
|
||||
_logger?.LogInformation("Starting {Count} competing email consumers.", _concurrency);
|
||||
|
||||
await Task.WhenAll(_consumers.Select(c => c.InitAsync()));
|
||||
|
||||
_logger?.LogInformation("All {Count} email consumers started.", _concurrency);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await Task.WhenAll(_consumers.Select(async c => await c.DisposeAsync().AsTask()));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user