Skip to content

Commit d19bea3

Browse files
authored
Merge pull request #45 from LuccaSA/consumer-async-handler
Use AsyncEventHandler<MessagingEvent> for consumer message handling
2 parents fe8ac72 + 01dcf27 commit d19bea3

8 files changed

Lines changed: 37 additions & 26 deletions

File tree

samples/MerQure.Samples/DeadLetterExample.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ public async Task RunAsync()
5151

5252
// Get the consumer on the existing queue and consume its messages
5353
var consumer = await _messagingService.GetConsumerAsync("deadletter.queue");
54-
await consumer.ConsumeAsync((object sender, IMessagingEvent args) =>
54+
await consumer.ConsumeAsync((object sender, MessagingEvent args) =>
5555
{
5656
var realDelay = DateTime.Now.Subtract(dateStart).TotalSeconds;
5757
Console.WriteLine(string.Format("{0} received after {1:#.##}s.", args.Message.GetRoutingKey(), realDelay));
5858
// send ACK: acknowlegdment to the queue
59-
consumer.AcknowlegdeDeliveredMessageAsync(args);
59+
return consumer.AcknowlegdeDeliveredMessageAsync(args).AsTask();
6060
});
6161
}
6262
}

samples/MerQure.Samples/SimpleExample.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,20 @@ public async Task RunAsync()
3232
// Get the consumer on the existing queue and consume its messages
3333
var consumer = await _messagingService.GetConsumerAsync("simple.queue");
3434
var random = new Random();
35-
await consumer.ConsumeAsync((object sender, IMessagingEvent args) =>
35+
await consumer.ConsumeAsync((object sender, MessagingEvent args) =>
3636
{
3737
// we simulate the delivery success
3838
if (random.Next() % 2 == 0)
3939
{
4040
Console.WriteLine("Retry " + args.Message.GetRoutingKey());
4141
// send NACK: negative acknowlegdment to the queue
42-
consumer.RejectDeliveredMessageAsync(args);
42+
return consumer.RejectDeliveredMessageAsync(args).AsTask();
4343
}
4444
else
4545
{
4646
Console.WriteLine(args.Message.GetBody());
4747
// send ACK: acknowlegdment to the queue
48-
consumer.AcknowlegdeDeliveredMessageAsync(args);
48+
return consumer.AcknowlegdeDeliveredMessageAsync(args).AsTask();
4949
}
5050
});
5151
}

samples/MerQure.Samples/StopExample.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ await consumer.ConsumeAsync((_, args) =>
3939
Thread.Sleep(10);
4040
Console.WriteLine(args.Message.GetBody());
4141
// send ACK: acknowlegdment to the queue
42-
consumer.AcknowlegdeDeliveredMessageAsync(args);
42+
return consumer.AcknowlegdeDeliveredMessageAsync(args).AsTask();
4343
});
4444

4545
// Stop Consuming after 100 ms ~ 10 messages

src/MerQure.RbMQ/Clients/Consumer.cs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
using MerQure.Messages;
22
using MerQure.RbMQ.Content;
3-
using MerQure.RbMQ.Events;
43
using RabbitMQ.Client;
54
using RabbitMQ.Client.Events;
65
using System;
76
using System.Collections.Generic;
87
using System.Linq;
98
using System.Text;
9+
using System.Threading;
1010
using System.Threading.Tasks;
1111

1212
namespace MerQure.RbMQ.Clients;
@@ -17,33 +17,37 @@ class Consumer : RabbitMqClient, IConsumer
1717

1818
private AsyncEventingBasicConsumer _consumer;
1919
private readonly ushort _prefetchCount;
20-
private readonly object _consumingLock;
20+
private readonly SemaphoreSlim _consumingLock;
2121

2222
public Consumer(IChannel channel, string queueName, ushort prefetchCount)
2323
: base(channel)
2424
{
2525
QueueName = queueName.ToLowerInvariant();
2626
_prefetchCount = prefetchCount;
27-
_consumingLock = new object();
27+
_consumingLock = new SemaphoreSlim(1, 1);
2828
}
2929

30-
public async Task ConsumeAsync(EventHandler<IMessagingEvent> onMessageReceived)
30+
public async Task ConsumeAsync(AsyncEventHandler<MessagingEvent> onMessageReceived)
3131
{
3232
await Channel.BasicQosAsync(0, _prefetchCount, false);
3333

3434
_consumer = new AsyncEventingBasicConsumer(Channel);
35-
_consumer.ReceivedAsync += (sender, args) =>
35+
_consumer.ReceivedAsync += async (sender, args) =>
3636
{
3737
if (onMessageReceived != null)
3838
{
39-
lock (_consumingLock)
39+
await _consumingLock.WaitAsync();
40+
try
4041
{
4142
var message = ParseDeliveredMessage(args);
4243
var messageEventArgs = new MessagingEvent(message, args.DeliveryTag.ToString());
43-
onMessageReceived(sender, messageEventArgs);
44+
await onMessageReceived(sender, messageEventArgs);
45+
}
46+
finally
47+
{
48+
_consumingLock.Release();
4449
}
4550
}
46-
return Task.CompletedTask;
4751
};
4852

4953
await Channel.BasicConsumeAsync(QueueName, false, _consumer);
@@ -89,17 +93,18 @@ public async Task StopConsuming(AsyncEventHandler<ConsumerEventArgs> onConsumerS
8993
{
9094
if (IsConsuming())
9195
{
92-
lock (_consumingLock)
96+
await _consumingLock.WaitAsync();
97+
try
9398
{
9499
if (onConsumerStopped != null)
95100
{
96-
_consumer.UnregisteredAsync += (sender, e) =>
97-
{
98-
onConsumerStopped(sender, e);
99-
return Task.CompletedTask;
100-
};
101+
_consumer.UnregisteredAsync += onConsumerStopped;
101102
}
102103
}
104+
finally
105+
{
106+
_consumingLock.Release();
107+
}
103108

104109
// Must be outside the lock to avoid deadlock
105110
foreach (var tag in _consumer.ConsumerTags)

src/MerQure.Tools/Buses/Consumer.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public async Task ConsumeAsync(Channel channel, EventHandler<T> callback)
2424
await consumer.ConsumeAsync((_, messagingEvent) =>
2525
{
2626
OnMessageReceived(callback, messagingEvent);
27+
return Task.CompletedTask;
2728
});
2829
}
2930

src/MerQure/Clients/IConsumer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public interface IConsumer : IAsyncDisposable
1919
/// Start listening on the queue
2020
/// </summary>
2121
/// <param name="onMessageReceived">Handler called each time a message arrives for this consumer.</param>
22-
Task ConsumeAsync(EventHandler<IMessagingEvent> onMessageReceived);
22+
Task ConsumeAsync(AsyncEventHandler<MessagingEvent> onMessageReceived);
2323

2424
/// <summary>
2525
/// Indicates if the Consumer is registred on the queue and waiting for messages
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
namespace MerQure.RbMQ.Events
1+
using MerQure.Messages;
2+
using RabbitMQ.Client.Events;
3+
4+
namespace MerQure
25
{
3-
class MessagingEvent : IMessagingEvent
6+
public class MessagingEvent : AsyncEventArgs, IMessagingEvent
47
{
58
public IMessage Message { get; set; }
69

tests/MerQure.Tools.Tests/Buses/ConsumerTests.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
using MerQure.Tools.Buses;
1+
using MerQure.Messages;
2+
using MerQure.Tools.Buses;
23
using MerQure.Tools.Configurations;
34
using MerQure.Tools.Messages;
45
using Moq;
56
using Newtonsoft.Json;
7+
using RabbitMQ.Client.Events;
68
using System;
79
using System.Threading.Tasks;
810
using Xunit;
@@ -18,9 +20,9 @@ public class ConsumerTests : IDisposable
1820
public ConsumerTests()
1921
{
2022
_mockMerQureConsumer = new Mock<IConsumer>();
21-
_mockMerQureConsumer.Setup(m => m.ConsumeAsync(It.IsAny<EventHandler<IMessagingEvent>>())).Callback((EventHandler<IMessagingEvent> action) =>
23+
_mockMerQureConsumer.Setup(m => m.ConsumeAsync(It.IsAny<AsyncEventHandler<MessagingEvent>>())).Callback((AsyncEventHandler<MessagingEvent> action) =>
2224
{
23-
action(this, new Mock<IMessagingEvent>().Object);
25+
action(this, new MessagingEvent(new Mock<IMessage>().Object, "1"));
2426
});
2527

2628
_mockMessagingService = new Mock<IMessagingService>();

0 commit comments

Comments
 (0)