parent
c6a36c055d
commit
f2e12b7f7e
@ -0,0 +1,5 @@
|
||||
// Global using directives
|
||||
|
||||
global using Microsoft.Extensions.Logging;
|
||||
global using Newtonsoft.Json;
|
||||
global using RabbitMQ.Client;
|
@ -0,0 +1,6 @@
|
||||
namespace Infrastructure.EventBus;
|
||||
|
||||
public interface IDynamicIntegrationEventHandler
|
||||
{
|
||||
Task Handle(dynamic eventData);
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
namespace Infrastructure.EventBus;
|
||||
|
||||
public interface IIntegrationEventHandler
|
||||
{
|
||||
}
|
||||
|
||||
public interface IIntegrationEventHandler<in TIntegrationEvent> : IIntegrationEventHandler
|
||||
where TIntegrationEvent : IntegrationEvent
|
||||
{
|
||||
Task Handle(TIntegrationEvent integrationEvent);
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
namespace Infrastructure.EventBus;
|
||||
|
||||
[method: JsonConstructor]
|
||||
public class IntegrationEvent(Guid id, DateTime createdDate)
|
||||
{
|
||||
public IntegrationEvent() : this(Guid.NewGuid(), DateTime.UtcNow)
|
||||
{
|
||||
}
|
||||
|
||||
[JsonProperty] public Guid Id { get; private set; } = id;
|
||||
|
||||
[JsonProperty] public DateTime CreatedDate { get; private set; } = createdDate;
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
namespace Infrastructure.EventBus.RabbitMQ;
|
||||
|
||||
public interface IRabbitMQPersistentConnection : IDisposable
|
||||
{
|
||||
bool IsConnected { get; }
|
||||
|
||||
bool TryConnect();
|
||||
|
||||
IModel CreateModel();
|
||||
|
||||
void PublishMessage(string message, string exchangeName, string routingKey);
|
||||
|
||||
void StartConsuming(string queueName);
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using Polly;
|
||||
using Polly.Retry;
|
||||
using RabbitMQ.Client.Events;
|
||||
using RabbitMQ.Client.Exceptions;
|
||||
|
||||
namespace Infrastructure.EventBus.RabbitMQ;
|
||||
|
||||
public class RabbitMQPersistentConnection(
|
||||
IConnectionFactory connectionFactory,
|
||||
ILogger<RabbitMQPersistentConnection> logger,
|
||||
int retryCount = 5)
|
||||
: IRabbitMQPersistentConnection
|
||||
{
|
||||
private IConnection? _connection;
|
||||
private bool _disposed;
|
||||
private object sync_root = new object();
|
||||
|
||||
public bool IsConnected => _connection != null && _connection.IsOpen && !_disposed;
|
||||
|
||||
public bool TryConnect()
|
||||
{
|
||||
logger.LogInformation("RabbitMQ Client is trying to connect");
|
||||
|
||||
lock (sync_root)
|
||||
{
|
||||
var policy = RetryPolicy.Handle<SocketException>()
|
||||
.Or<BrokerUnreachableException>()
|
||||
.WaitAndRetry(retryCount,
|
||||
retryAttempt =>
|
||||
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
|
||||
(ex, time) =>
|
||||
{
|
||||
logger.LogWarning(ex,
|
||||
"RabbitMQ Client could not connect after {TimeOut}s ({ExceptionMessage})",
|
||||
$"{time.TotalSeconds:n1}", ex.Message);
|
||||
}
|
||||
);
|
||||
|
||||
policy.Execute(() =>
|
||||
{
|
||||
_connection = connectionFactory
|
||||
.CreateConnection();
|
||||
});
|
||||
|
||||
if (IsConnected)
|
||||
{
|
||||
_connection.ConnectionShutdown += OnConnectionShutdown;
|
||||
_connection.CallbackException += OnCallbackException;
|
||||
_connection.ConnectionBlocked += OnConnectionBlocked;
|
||||
|
||||
logger.LogInformation(
|
||||
"RabbitMQ Client acquired a persistent connection to '{HostName}' and is subscribed to failure events",
|
||||
_connection.Endpoint.HostName);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogCritical("FATAL ERROR: RabbitMQ connections could not be created and opened");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IModel CreateModel()
|
||||
{
|
||||
if (!IsConnected)
|
||||
{
|
||||
throw new InvalidOperationException("No RabbitMQ connections are available to perform this action");
|
||||
}
|
||||
|
||||
return _connection.CreateModel();
|
||||
}
|
||||
|
||||
public void PublishMessage(string message, string exchangeName, string routingKey)
|
||||
{
|
||||
using var channel = CreateModel();
|
||||
channel.ExchangeDeclare(exchange: exchangeName, type: ExchangeType.Direct, true);
|
||||
var body = Encoding.UTF8.GetBytes(message);
|
||||
channel.BasicPublish(exchange: exchangeName, routingKey: routingKey, basicProperties: null, body: body);
|
||||
}
|
||||
|
||||
public void StartConsuming(string queueName)
|
||||
{
|
||||
using var channel = CreateModel();
|
||||
channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: null);
|
||||
|
||||
var consumer = new AsyncEventingBasicConsumer(channel);
|
||||
consumer.Received += new AsyncEventHandler<BasicDeliverEventArgs>(
|
||||
async (a, b) =>
|
||||
{
|
||||
var Headers = b.BasicProperties.Headers;
|
||||
var msgBody = b.Body.ToArray();
|
||||
var message = Encoding.UTF8.GetString(msgBody);
|
||||
await Task.CompletedTask;
|
||||
Console.WriteLine("Received message: {0}", message);
|
||||
}
|
||||
);
|
||||
|
||||
channel.BasicConsume(queue: queueName, autoAck: true, consumer: consumer);
|
||||
|
||||
Console.WriteLine("Consuming messages...");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
_disposed = true;
|
||||
|
||||
try
|
||||
{
|
||||
_connection.Dispose();
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.LogCritical(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void OnConnectionBlocked(object sender, ConnectionBlockedEventArgs e)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogWarning("A RabbitMQ connection is shutdown. Trying to re-connect...");
|
||||
|
||||
TryConnect();
|
||||
}
|
||||
|
||||
private void OnCallbackException(object sender, CallbackExceptionEventArgs e)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogWarning("A RabbitMQ connection throw exception. Trying to re-connect...");
|
||||
|
||||
TryConnect();
|
||||
}
|
||||
|
||||
private void OnConnectionShutdown(object sender, ShutdownEventArgs reason)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogWarning("A RabbitMQ connection is on shutdown. Trying to re-connect...");
|
||||
|
||||
TryConnect();
|
||||
}
|
||||
}
|
Loading…
Reference in new issue