Files
xEventService/Providers/XRabbitMQProducerService.cs
T

295 lines
8.2 KiB
C#

using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using RabbitMQ.Client;
using xCommons.Extensions;
using xEventService.Configuration;
using xEventService.Constants;
using xEventService.Extensions;
using xEventService.Interfaces;
using xEventService.Models;
using xExceptions.Constants;
namespace xEventService.Providers
{
/// <summary>
/// RabbitMQ Producer Service Implementation ...
/// </summary>
public class XRabbitMQProducerService : IXRabbitMQProducerService
{
private IModel channel;
private IConnection connection;
private readonly object connectionLock = new();
private readonly ILogger<XRabbitMQProducerService> logger;
private readonly XEventServiceConfiguration configuration;
public XRabbitMQProducerService(
ILogger<XRabbitMQProducerService> logger,
XEventServiceConfiguration configuration
)
{
//
this.logger = logger;
this.configuration = configuration;
//
// Validate Configurations ...
if (!configuration.IsValid() ||
!configuration.RabbitMQ.IsValid() ||
configuration.Broker != XEventBroker.XRabbitMQ
)
{
XException.InvalidConfiguration.Throw();
}
//
// Initialize Connection ...
CreateConnection();
}
/// <summary>
/// Sending Message with Full Request Descriptor ...
/// </summary>
public async Task<bool> SendMessageAsync(
XEventRequest request,
CancellationToken cancellationToken = default
)
{
//
// Validate ...
var result = request.IsValid();
if (!result)
{
XException.InvalidArgs.Throw();
}
//
result = await SendMessageAsync(
message: request.Message,
exchange: request.Topic,
cancellationToken: cancellationToken,
routingKey: configuration.RabbitMQ.RoutingKey
);
//
return result;
}
/// <summary>
/// Sending a Message to Exchange ...
/// </summary>
public async Task<bool> SendMessageAsync(
XEventMessage message,
string exchange = null,
string routingKey = null,
CancellationToken cancellationToken = default
)
{
//
// Validate ...
var result = message.IsValid();
if (!result)
{
XException.InvalidArgs.Throw();
}
//
// Normalize Exchange ...
if (exchange.IsNullOrEmpty())
{
exchange = configuration.RabbitMQ.Exchange;
}
//
// Normalize Routing Key ...
if (routingKey.IsNull())
{
routingKey = configuration.RabbitMQ.RoutingKey;
}
//
try
{
//
// Ensure Connection is Alive ...
await EnsureChannelAsync(cancellationToken);
//
// Serialize Message ...
var body = Encoding.UTF8.GetBytes(
message.ToJSON(camelCase: true)
);
//
// Prepare Message Properties ...
var properties = channel.CreateBasicProperties();
properties.Persistent = true;
properties.ContentType = "application/json";
properties.Timestamp = new AmqpTimestamp(
DateTimeOffset.UtcNow.ToUnixTimeSeconds()
);
properties.MessageId = Guid.NewGuid().ToString();
properties.Type = message.Action;
properties.AppId = message.Sender;
//
// Publish Message ...
await Task.Run(() =>
{
//
channel.BasicPublish(
exchange: exchange,
routingKey: routingKey,
mandatory: false,
basicProperties: properties,
body: body
);
},
cancellationToken: cancellationToken);
//
result = true;
}
catch
{
result = false;
}
//
return result;
}
/// <summary>
/// Dispose Object ...
/// </summary>
public void Dispose()
{
//
try
{
//
channel.Close();
channel.Dispose();
channel = null;
//
connection.Close();
connection.Dispose();
connection = null;
}
catch
{ }
//
GC.SuppressFinalize(this);
}
//
#region Private ...
/// <summary>
/// Create Connection to RabbitMQ Server ...
/// </summary>
private void CreateConnection()
{
//
var factory = new ConnectionFactory
{
Port = configuration.GetRabbitMQPort(),
HostName = configuration.GetRabbitMQHost(),
UserName = configuration.RabbitMQ.Username,
Password = configuration.RabbitMQ.Password,
VirtualHost = configuration.RabbitMQ.VirtualHost,
AutomaticRecoveryEnabled = configuration.RabbitMQ.AutomaticRecoveryEnabled,
NetworkRecoveryInterval = TimeSpan.FromSeconds(
configuration.RabbitMQ.NetworkRecoveryIntervalSeconds
),
DispatchConsumersAsync = true
};
//
connection = factory.CreateConnection();
}
/// <summary>
/// Check Connection is Alive ...
/// </summary>
private bool IsConnected()
{
//
var result =
!connection.IsNullOrDefault() &&
connection.IsOpen &&
!channel.IsNull() &&
channel.IsOpen;
//
return result;
}
/// <summary>
/// Ensure Connection Established ...
/// </summary>
private async Task<bool> EnsureConnectionAsync()
{
//
try
{
await EnsureChannelAsync();
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Ensure Channel is Open and Exchange/Queue Declared ...
/// </summary>
private async Task EnsureChannelAsync(
CancellationToken cancellationToken = default
)
{
//
if (IsConnected())
{
return;
}
//
lock (connectionLock)
{
//
// Double Check after Lock ...
if (IsConnected())
{
return;
}
//
// Recreate Connection if Dead ...
if (connection is null || !connection.IsOpen)
{
CreateConnection();
}
//
// Create Channel ...
channel = connection.CreateModel();
//
// Declare Exchange ...
channel.ExchangeDeclare(
arguments: null,
exchange: configuration.RabbitMQ.Exchange,
durable: configuration.RabbitMQ.ExchangeDurable,
autoDelete: configuration.RabbitMQ.ExchangeAutoDelete,
type: configuration.RabbitMQ.ExchangeType.GetStringValue()
);
}
}
#endregion
}
}