Merge pull request #240 from GoFightNguyen/master
dotnet tutorial of PublisherConfirms
This commit is contained in:
commit
4609af962f
138
dotnet/PublisherConfirms/PublisherConfirms.cs
Normal file
138
dotnet/PublisherConfirms/PublisherConfirms.cs
Normal file
@ -0,0 +1,138 @@
|
||||
using RabbitMQ.Client;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
class PublisherConfirms
|
||||
{
|
||||
private const int MESSAGE_COUNT = 50_000;
|
||||
|
||||
public static void Main()
|
||||
{
|
||||
PublishMessagesIndividually();
|
||||
PublishMessagesInBatch();
|
||||
HandlePublishConfirmsAsynchronously();
|
||||
}
|
||||
|
||||
private static IConnection CreateConnection()
|
||||
{
|
||||
var factory = new ConnectionFactory { HostName = "localhost" };
|
||||
return factory.CreateConnection();
|
||||
}
|
||||
|
||||
private static void PublishMessagesIndividually()
|
||||
{
|
||||
using (var connection = CreateConnection())
|
||||
using (var channel = connection.CreateModel())
|
||||
{
|
||||
var queueName = channel.QueueDeclare().QueueName;
|
||||
channel.ConfirmSelect();
|
||||
|
||||
var timer = new Stopwatch();
|
||||
timer.Start();
|
||||
for (int i = 0; i < MESSAGE_COUNT; i++)
|
||||
{
|
||||
var body = Encoding.UTF8.GetBytes(i.ToString());
|
||||
channel.BasicPublish(exchange: "", routingKey: queueName, basicProperties: null, body: body);
|
||||
channel.WaitForConfirmsOrDie(new TimeSpan(0, 0, 5));
|
||||
}
|
||||
timer.Stop();
|
||||
Console.WriteLine($"Published {MESSAGE_COUNT:N0} messages individually in {timer.ElapsedMilliseconds:N0} ms");
|
||||
}
|
||||
}
|
||||
|
||||
private static void PublishMessagesInBatch()
|
||||
{
|
||||
using (var connection = CreateConnection())
|
||||
using (var channel = connection.CreateModel())
|
||||
{
|
||||
var queueName = channel.QueueDeclare().QueueName;
|
||||
channel.ConfirmSelect();
|
||||
|
||||
var batchSize = 100;
|
||||
var outstandingMessageCount = 0;
|
||||
var timer = new Stopwatch();
|
||||
timer.Start();
|
||||
for (int i = 0; i < MESSAGE_COUNT; i++)
|
||||
{
|
||||
var body = Encoding.UTF8.GetBytes(i.ToString());
|
||||
channel.BasicPublish(exchange: "", routingKey: queueName, basicProperties: null, body: body);
|
||||
outstandingMessageCount++;
|
||||
|
||||
if (outstandingMessageCount == batchSize)
|
||||
{
|
||||
channel.WaitForConfirmsOrDie(new TimeSpan(0, 0, 5));
|
||||
outstandingMessageCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (outstandingMessageCount > 0)
|
||||
channel.WaitForConfirmsOrDie(new TimeSpan(0, 0, 5));
|
||||
|
||||
timer.Stop();
|
||||
Console.WriteLine($"Published {MESSAGE_COUNT:N0} messages in batch in {timer.ElapsedMilliseconds:N0} ms");
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandlePublishConfirmsAsynchronously()
|
||||
{
|
||||
using (var connection = CreateConnection())
|
||||
using (var channel = connection.CreateModel())
|
||||
{
|
||||
var queueName = channel.QueueDeclare().QueueName;
|
||||
channel.ConfirmSelect();
|
||||
|
||||
var outstandingConfirms = new ConcurrentDictionary<ulong, string>();
|
||||
|
||||
void cleanOutstandingConfirms(ulong sequenceNumber, bool multiple)
|
||||
{
|
||||
if (multiple)
|
||||
{
|
||||
var confirmed = outstandingConfirms.Where(k => k.Key <= sequenceNumber);
|
||||
foreach (var entry in confirmed)
|
||||
outstandingConfirms.TryRemove(entry.Key, out _);
|
||||
}
|
||||
else
|
||||
outstandingConfirms.TryRemove(sequenceNumber, out _);
|
||||
}
|
||||
|
||||
channel.BasicAcks += (sender, ea) => cleanOutstandingConfirms(ea.DeliveryTag, ea.Multiple);
|
||||
channel.BasicNacks += (sender, ea) =>
|
||||
{
|
||||
outstandingConfirms.TryGetValue(ea.DeliveryTag, out string body);
|
||||
Console.WriteLine($"Message with body {body} has been nack-ed. Sequence number: {ea.DeliveryTag}, multiple: {ea.Multiple}");
|
||||
cleanOutstandingConfirms(ea.DeliveryTag, ea.Multiple);
|
||||
};
|
||||
|
||||
var timer = new Stopwatch();
|
||||
timer.Start();
|
||||
for (int i = 0; i < MESSAGE_COUNT; i++)
|
||||
{
|
||||
var body = i.ToString();
|
||||
outstandingConfirms.TryAdd(channel.NextPublishSeqNo, i.ToString());
|
||||
channel.BasicPublish(exchange: "", routingKey: queueName, basicProperties: null, body: Encoding.UTF8.GetBytes(body));
|
||||
}
|
||||
|
||||
if (!WaitUntil(60, () => outstandingConfirms.IsEmpty))
|
||||
throw new Exception("All messages could not be confirmed in 60 seconds");
|
||||
|
||||
timer.Stop();
|
||||
Console.WriteLine($"Published {MESSAGE_COUNT:N0} messages and handled confirm asynchronously {timer.ElapsedMilliseconds:N0} ms");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool WaitUntil(int numberOfSeconds, Func<bool> condition)
|
||||
{
|
||||
int waited = 0;
|
||||
while(!condition() && waited < numberOfSeconds * 1000)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
waited += 100;
|
||||
}
|
||||
|
||||
return condition();
|
||||
}
|
||||
}
|
15
dotnet/PublisherConfirms/PublisherConfirms.csproj
Normal file
15
dotnet/PublisherConfirms/PublisherConfirms.csproj
Normal file
@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<DebugType>portable</DebugType>
|
||||
<AssemblyName>PublisherConfirms</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PackageId>PublisherConfirms</PackageId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RabbitMQ.Client" Version="5.1.*" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Loading…
Reference in New Issue
Block a user