mirror of
https://github.com/alex289/CleanArchitecture.git
synced 2025-06-30 10:33:43 +00:00
feat: Add rabbitmq
This commit is contained in:
parent
6d4b000bd0
commit
4746bfbef1
@ -3,6 +3,7 @@ using CleanArchitecture.Api.Extensions;
|
|||||||
using CleanArchitecture.Application.Extensions;
|
using CleanArchitecture.Application.Extensions;
|
||||||
using CleanArchitecture.Application.gRPC;
|
using CleanArchitecture.Application.gRPC;
|
||||||
using CleanArchitecture.Domain.Extensions;
|
using CleanArchitecture.Domain.Extensions;
|
||||||
|
using CleanArchitecture.Domain.Rabbitmq.Extensions;
|
||||||
using CleanArchitecture.Infrastructure.Database;
|
using CleanArchitecture.Infrastructure.Database;
|
||||||
using CleanArchitecture.Infrastructure.Extensions;
|
using CleanArchitecture.Infrastructure.Extensions;
|
||||||
using HealthChecks.ApplicationStatus.DependencyInjection;
|
using HealthChecks.ApplicationStatus.DependencyInjection;
|
||||||
@ -51,6 +52,8 @@ builder.Services.AddCommandHandlers();
|
|||||||
builder.Services.AddNotificationHandlers();
|
builder.Services.AddNotificationHandlers();
|
||||||
builder.Services.AddApiUser();
|
builder.Services.AddApiUser();
|
||||||
|
|
||||||
|
builder.Services.AddRabbitMqHandler(builder.Configuration, "RabbitMQ");
|
||||||
|
|
||||||
builder.Services.AddHostedService<SetInactiveUsersService>();
|
builder.Services.AddHostedService<SetInactiveUsersService>();
|
||||||
|
|
||||||
builder.Services.AddMediatR(cfg => { cfg.RegisterServicesFromAssemblies(typeof(Program).Assembly); });
|
builder.Services.AddMediatR(cfg => { cfg.RegisterServicesFromAssemblies(typeof(Program).Assembly); });
|
||||||
|
@ -12,5 +12,11 @@
|
|||||||
"Issuer": "CleanArchitectureServer",
|
"Issuer": "CleanArchitectureServer",
|
||||||
"Audience": "CleanArchitectureClient",
|
"Audience": "CleanArchitectureClient",
|
||||||
"Secret": "sD3v061gf8BxXgmxcHssasjdlkasjd87439284)@#(*"
|
"Secret": "sD3v061gf8BxXgmxcHssasjdlkasjd87439284)@#(*"
|
||||||
|
},
|
||||||
|
"RabbitMQ": {
|
||||||
|
"Host": "localhost",
|
||||||
|
"Username": "guest",
|
||||||
|
"Password": "guest",
|
||||||
|
"Enabled": "True"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
CleanArchitecture.Api/appsettings.Integration.json
Normal file
14
CleanArchitecture.Api/appsettings.Integration.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"RabbitMQ": {
|
||||||
|
"Host": "localhost",
|
||||||
|
"Username": "guest",
|
||||||
|
"Password": "guest",
|
||||||
|
"Enabled": "False"
|
||||||
|
}
|
||||||
|
}
|
@ -14,5 +14,11 @@
|
|||||||
"Audience": "CleanArchitectureClient",
|
"Audience": "CleanArchitectureClient",
|
||||||
"Secret": "sD3v061gf8BxXgmxcHssasjdlkasjd87439284)@#(*"
|
"Secret": "sD3v061gf8BxXgmxcHssasjdlkasjd87439284)@#(*"
|
||||||
},
|
},
|
||||||
"RedisHostName": "redis"
|
"RedisHostName": "redis",
|
||||||
|
"RabbitMQ": {
|
||||||
|
"Host": "rabbitmq",
|
||||||
|
"Username": "guest",
|
||||||
|
"Password": "guest",
|
||||||
|
"Enabled": "True"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
<PackageReference Include="MediatR" Version="12.1.1"/>
|
<PackageReference Include="MediatR" Version="12.1.1"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1"/>
|
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1"/>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="RabbitMQ.Client" Version="6.5.0" />
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.32.1"/>
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.32.1"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
|
||||||
|
public sealed class BindQueueToExchange : IRabbitMqAction
|
||||||
|
{
|
||||||
|
private readonly string _exchangeName;
|
||||||
|
private readonly string _queueName;
|
||||||
|
private readonly string _routingKey;
|
||||||
|
|
||||||
|
public BindQueueToExchange(string queueName, string exchangeName, string routingKey = "")
|
||||||
|
{
|
||||||
|
_exchangeName = exchangeName;
|
||||||
|
_routingKey = routingKey;
|
||||||
|
_queueName = queueName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Perform(IModel channel)
|
||||||
|
{
|
||||||
|
channel.QueueBind(_queueName, _exchangeName, _routingKey);
|
||||||
|
}
|
||||||
|
}
|
20
CleanArchitecture.Domain/Rabbitmq/Actions/CreateExchange.cs
Normal file
20
CleanArchitecture.Domain/Rabbitmq/Actions/CreateExchange.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
|
||||||
|
public sealed class CreateExchange : IRabbitMqAction
|
||||||
|
{
|
||||||
|
private readonly string _name;
|
||||||
|
private readonly string _type;
|
||||||
|
|
||||||
|
public CreateExchange(string name, string type)
|
||||||
|
{
|
||||||
|
_name = name;
|
||||||
|
_type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Perform(IModel channel)
|
||||||
|
{
|
||||||
|
channel.ExchangeDeclare(_name, _type);
|
||||||
|
}
|
||||||
|
}
|
23
CleanArchitecture.Domain/Rabbitmq/Actions/CreateQueue.cs
Normal file
23
CleanArchitecture.Domain/Rabbitmq/Actions/CreateQueue.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
|
||||||
|
public sealed class CreateQueue : IRabbitMqAction
|
||||||
|
{
|
||||||
|
public string QueueName { get; }
|
||||||
|
|
||||||
|
public CreateQueue(string queueName)
|
||||||
|
{
|
||||||
|
QueueName = queueName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Perform(IModel channel)
|
||||||
|
{
|
||||||
|
channel.QueueDeclare(
|
||||||
|
QueueName,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
|
||||||
|
public interface IRabbitMqAction
|
||||||
|
{
|
||||||
|
void Perform(IModel channel);
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
|
||||||
|
public sealed class RegisterConsumer : IRabbitMqAction
|
||||||
|
{
|
||||||
|
private readonly Action<string, string, string, ConsumeEventHandler> _addConsumer;
|
||||||
|
private readonly ConsumeEventHandler _consumer;
|
||||||
|
private readonly string _exchange;
|
||||||
|
private readonly string _queue;
|
||||||
|
private readonly string _routingKey;
|
||||||
|
|
||||||
|
public RegisterConsumer(
|
||||||
|
string exchange,
|
||||||
|
string queue,
|
||||||
|
string routingKey,
|
||||||
|
ConsumeEventHandler consumer,
|
||||||
|
Action<string, string, string, ConsumeEventHandler> addConsumer)
|
||||||
|
{
|
||||||
|
_exchange = exchange;
|
||||||
|
_queue = queue;
|
||||||
|
_routingKey = routingKey;
|
||||||
|
_consumer = consumer;
|
||||||
|
_addConsumer = addConsumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Perform(IModel channel)
|
||||||
|
{
|
||||||
|
_addConsumer(_exchange, _queue, _routingKey, _consumer);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
|
||||||
|
public sealed class SendAcknowledgement : IRabbitMqAction
|
||||||
|
{
|
||||||
|
public ulong DeliveryTag { get; }
|
||||||
|
|
||||||
|
public SendAcknowledgement(ulong deliveryTag)
|
||||||
|
{
|
||||||
|
DeliveryTag = deliveryTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Perform(IModel channel)
|
||||||
|
{
|
||||||
|
channel.BasicAck(DeliveryTag, false);
|
||||||
|
}
|
||||||
|
}
|
38
CleanArchitecture.Domain/Rabbitmq/Actions/SendMessage.cs
Normal file
38
CleanArchitecture.Domain/Rabbitmq/Actions/SendMessage.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
|
||||||
|
public sealed class SendMessage : IRabbitMqAction
|
||||||
|
{
|
||||||
|
private static readonly JsonSerializerSettings s_serializerSettings =
|
||||||
|
new() { TypeNameHandling = TypeNameHandling.Objects };
|
||||||
|
|
||||||
|
private readonly string _exchange;
|
||||||
|
private readonly object _message;
|
||||||
|
|
||||||
|
private readonly string _routingKey;
|
||||||
|
|
||||||
|
|
||||||
|
/// <param name="routingKey">If exchange is empty, this is the name of the queue</param>
|
||||||
|
public SendMessage(string routingKey, string exchange, object message)
|
||||||
|
{
|
||||||
|
_routingKey = routingKey;
|
||||||
|
_exchange = exchange;
|
||||||
|
_message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Perform(IModel channel)
|
||||||
|
{
|
||||||
|
var json = JsonConvert.SerializeObject(_message, s_serializerSettings);
|
||||||
|
|
||||||
|
var content = Encoding.UTF8.GetBytes(json);
|
||||||
|
|
||||||
|
channel.BasicPublish(
|
||||||
|
_exchange,
|
||||||
|
_routingKey,
|
||||||
|
null,
|
||||||
|
content);
|
||||||
|
}
|
||||||
|
}
|
6
CleanArchitecture.Domain/Rabbitmq/Delegates.cs
Normal file
6
CleanArchitecture.Domain/Rabbitmq/Delegates.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq;
|
||||||
|
|
||||||
|
public delegate Task<bool> ConsumeEventHandler(ReadOnlyMemory<byte> content);
|
@ -0,0 +1,22 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Extensions;
|
||||||
|
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddRabbitMqHandler(
|
||||||
|
this IServiceCollection services,
|
||||||
|
IConfiguration configuration,
|
||||||
|
string rabbitMqConfigSection)
|
||||||
|
{
|
||||||
|
var rabbitMq = new RabbitMqConfiguration();
|
||||||
|
configuration.Bind(rabbitMqConfigSection, rabbitMq);
|
||||||
|
services.AddSingleton(rabbitMq);
|
||||||
|
|
||||||
|
services.AddSingleton<RabbitMqHandler>();
|
||||||
|
services.AddHostedService(serviceProvider => serviceProvider.GetService<RabbitMqHandler>()!);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
namespace CleanArchitecture.Domain.Rabbitmq;
|
||||||
|
|
||||||
|
public sealed class RabbitMqConfiguration
|
||||||
|
{
|
||||||
|
public string Host { get; set; } = string.Empty;
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
public string Username { get; set; } = string.Empty;
|
||||||
|
public string Password { get; set; } = string.Empty;
|
||||||
|
}
|
228
CleanArchitecture.Domain/Rabbitmq/RabbitMqHandler.cs
Normal file
228
CleanArchitecture.Domain/Rabbitmq/RabbitMqHandler.cs
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
using RabbitMQ.Client.Events;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq;
|
||||||
|
|
||||||
|
public sealed class RabbitMqHandler : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly RabbitMqConfiguration _configuration;
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<string, List<ConsumeEventHandler>> _consumers = new();
|
||||||
|
|
||||||
|
private readonly ILogger<RabbitMqHandler> _logger;
|
||||||
|
|
||||||
|
private readonly ConcurrentQueue<IRabbitMqAction> _pendingActions = new();
|
||||||
|
|
||||||
|
private readonly IModel? _channel;
|
||||||
|
|
||||||
|
private readonly IConnection? _connection;
|
||||||
|
|
||||||
|
|
||||||
|
public RabbitMqHandler(
|
||||||
|
RabbitMqConfiguration configuration,
|
||||||
|
ILogger<RabbitMqHandler> logger)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
if (!configuration.Enabled)
|
||||||
|
{
|
||||||
|
logger.LogInformation("RabbitMQ is disabled. Connection will not be established");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var factory = new ConnectionFactory
|
||||||
|
{
|
||||||
|
AutomaticRecoveryEnabled = true,
|
||||||
|
HostName = configuration.Host,
|
||||||
|
UserName = configuration.Username,
|
||||||
|
Password = configuration.Password,
|
||||||
|
DispatchConsumersAsync = true
|
||||||
|
};
|
||||||
|
|
||||||
|
_connection = factory.CreateConnection();
|
||||||
|
_channel = _connection.CreateModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializeExchange(string exchangeName, string type = ExchangeType.Fanout)
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"RabbitMQ is disabled. Skipping the creation of exchange {exchangeName}.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingActions.Enqueue(new CreateExchange(exchangeName, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializeQueues(params string[] queueNames)
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RabbitMQ is disabled. Skipping the creation of queues.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var queue in queueNames)
|
||||||
|
{
|
||||||
|
_pendingActions.Enqueue(new CreateQueue(queue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BindQueueToExchange(string queueName, string exchangeName, string routingKey = "")
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RabbitMQ is disabled. Skipping the binding of queue to exchange.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingActions.Enqueue(new BindQueueToExchange(queueName, exchangeName, routingKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddConsumer(string queueName, ConsumeEventHandler consumer)
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RabbitMQ is disabled. Skipping the addition of consumer.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// routingKey is set to queueName to mimic rabbitMQ
|
||||||
|
_pendingActions.Enqueue(
|
||||||
|
new RegisterConsumer(
|
||||||
|
string.Empty,
|
||||||
|
queueName,
|
||||||
|
queueName,
|
||||||
|
consumer,
|
||||||
|
AddEventConsumer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddExchangeConsumer(string exchange, string routingKey, string queue, ConsumeEventHandler consumer)
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RabbitMQ is disabled. Skipping the addition of exchange consumer.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingActions.Enqueue(
|
||||||
|
new RegisterConsumer(
|
||||||
|
exchange,
|
||||||
|
queue,
|
||||||
|
routingKey,
|
||||||
|
consumer,
|
||||||
|
AddEventConsumer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddExchangeConsumer(string exchange, string queue, ConsumeEventHandler consumer)
|
||||||
|
{
|
||||||
|
AddExchangeConsumer(exchange, string.Empty, queue, consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddEventConsumer(string exchange, string queueName, string routingKey, ConsumeEventHandler consumer)
|
||||||
|
{
|
||||||
|
var key = $"{exchange}-{routingKey}";
|
||||||
|
|
||||||
|
if (!_consumers.TryGetValue(key, out var consumers))
|
||||||
|
{
|
||||||
|
consumers = new List<ConsumeEventHandler>();
|
||||||
|
_consumers.TryAdd(key, consumers);
|
||||||
|
|
||||||
|
var eventHandler = new AsyncEventingBasicConsumer(_channel);
|
||||||
|
eventHandler.Received += CallEventConsumersAsync;
|
||||||
|
|
||||||
|
_channel!.BasicConsume(queueName, false, eventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
consumers.Add(consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CallEventConsumersAsync(object sender, BasicDeliverEventArgs ea)
|
||||||
|
{
|
||||||
|
var key = $"{ea.Exchange}-{ea.RoutingKey}";
|
||||||
|
|
||||||
|
if (!_consumers.TryGetValue(key, out var consumers))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var consumer in consumers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await consumer(ea.Body);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, $"Error while handling event in queue {ea.RoutingKey}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingActions.Enqueue(new SendAcknowledgement(ea.DeliveryTag));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void EnqueueMessage(string queueName, object message)
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RabbitMQ is disabled. Skipping enqueueing of message");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingActions.Enqueue(new SendMessage(queueName, string.Empty, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnqueueExchangeMessage(string exchange, object message, string routingKey = "")
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RabbitMQ is disabled. Skipping enqueueing of message");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingActions.Enqueue(new SendMessage(routingKey, exchange, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RabbitMQ is disabled. Message handling loop will not be started");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
HandleEnqueuedActions();
|
||||||
|
|
||||||
|
await Task.Delay(1000, stoppingToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleEnqueuedActions()
|
||||||
|
{
|
||||||
|
while (_pendingActions.TryDequeue(out var action))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
action.Perform(_channel!);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error while trying to send a rabbitmq message");
|
||||||
|
_pendingActions.Enqueue(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user