using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using xCommons.Extensions;
using xEventService.Configuration;
using xEventService.Constants;
using xEventService.Extensions;
using xEventService.Models;
using xExceptions.Constants;
namespace xEventService.Providers
{
///
/// an Abstract Kafka Consumer Service ...
///
public abstract class XRabbitMQConsumerServiceBase : XBaseEventConsumerBackgroundService
{
protected IModel channel;
protected IConnection connection;
protected AsyncEventingBasicConsumer consumer;
protected XRabbitMQConsumerServiceBase(
string[] allowedActions,
string[] allowedSenders,
IServiceProvider serviceProvider,
XEventServiceConfiguration configuration,
ILogger logger,
string topic = XEventServiceConstants.XDefaultTopic
) : base(
topic: topic,
logger: logger,
configuration: configuration,
allowedActions: allowedActions,
allowedSenders: allowedSenders,
serviceProvider: serviceProvider
)
{
//
// Validating Configuration ...
if (!configuration.RabbitMQ.IsValid() ||
configuration.Broker != XEventBroker.XRabbitMQ
)
{
XException.InvalidConfiguration.Throw();
}
//
SetupConnection();
DeclareTopology();
consumer = new AsyncEventingBasicConsumer(channel);
}
///
/// Default Action Execution for Background Services ...
///
///
///
protected override async Task ExecuteAsync(
CancellationToken cancellationToken = default
)
{
//
// For Fix Blocking Synchronous ...
await Task.Yield();
using var cts = new CancellationTokenSource();
//
// Subscribe to Topic ...
consumer.Received += async (model, ea) =>
{
//
try
{
//
var body = ea.Body.ToArray();
var json = Encoding.UTF8.GetString(body);
var message = json.FromJSON();
if (!message.IsNullOrDefault())
{
//
await DoWorkAsync(
payload: message,
cancellationToken: cancellationToken
);
}
//
if (configuration.RabbitMQ.ManualAck)
{
//
channel.BasicAck(
multiple: false,
deliveryTag: ea.DeliveryTag
);
}
}
catch
{
//
if (configuration.RabbitMQ.ManualAck)
{
//
channel.BasicNack(
requeue: true,
multiple: false,
deliveryTag: ea.DeliveryTag
);
}
}
};
//
consumer.Shutdown += (model, ea) =>
{
return Task.CompletedTask;
};
//
channel.BasicQos(
prefetchSize: 0,
global: false,
prefetchCount: configuration.RabbitMQ.PrefetchCount
);
//
channel.BasicConsume(
queue: GetQueueName(),
consumer: consumer,
autoAck: !configuration.RabbitMQ.ManualAck
);
//
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(1000, cancellationToken);
}
}
///
/// Overriding Stop Action ...
///
///
///
public override async Task StopAsync(CancellationToken cancellationToken)
{
//
channel.Close();
channel.Dispose();
connection.Close();
connection.Dispose();
//
await base.StopAsync(cancellationToken);
}
///
/// Dispose Implementation ...
///
public override void Dispose()
{
//
channel.Dispose();
connection.Dispose();
GC.SuppressFinalize(this);
base.Dispose();
}
//
#region Private ...
private string GetQueueName()
{
//
var result = string.Empty;
if (!configuration.RabbitMQ.Queue.IsNullOrEmpty())
{
result = configuration.RabbitMQ.Queue;
}
//
if (result.IsNullOrEmpty())
{
//
var digits = Guid.NewGuid().ToString().GetDigits();
result = $"{configuration.RabbitMQ.Exchange}[{digits}]";
}
//
return result;
}
private void SetupConnection()
{
var factory = new ConnectionFactory
{
DispatchConsumersAsync = true,
Port = configuration.GetRabbitMQPort(),
HostName = configuration.GetRabbitMQHost(),
UserName = configuration.RabbitMQ.Username,
Password = configuration.RabbitMQ.Password,
NetworkRecoveryInterval = TimeSpan.FromSeconds(
configuration.RabbitMQ.NetworkRecoveryIntervalSeconds
),
VirtualHost = configuration.RabbitMQ.VirtualHost,
AutomaticRecoveryEnabled = configuration.RabbitMQ.AutomaticRecoveryEnabled,
};
//
connection = factory.CreateConnection();
channel = connection.CreateModel();
}
private void DeclareTopology()
{
//
channel.ExchangeDeclare(
arguments: null,
exchange: configuration.RabbitMQ.Exchange,
durable: configuration.RabbitMQ.ExchangeDurable,
autoDelete: configuration.RabbitMQ.ExchangeAutoDelete,
type: configuration.RabbitMQ.ExchangeType.GetStringValue()
);
//
var queueName = GetQueueName();
//
channel.QueueDeclare(
arguments: null,
queue: queueName,
durable: configuration.RabbitMQ.QueueDurable,
exclusive: configuration.RabbitMQ.QueueExclusive,
autoDelete: configuration.RabbitMQ.QueueAutoDelete
);
//
channel.QueueBind(
arguments: null,
queue: queueName,
exchange: configuration.RabbitMQ.Exchange,
routingKey: configuration.RabbitMQ.RoutingKey
);
}
#endregion
}
}