(Note: if you want to follow the code on this, you might find it easier if you start from the project that I create here.)
A queue in a message broker can be persistent, which means that, should you have a power failure (or just shut down the server), when it comes back, the queue is still there.
So, we can create a durable (persistent) queue, like this:
var result = channel.QueueDeclare("NewQueue", true, false, false, args);
The second parameter indicates that the queue is durable. Let’s send it some messages:
static void Main(string[] args)
{
for (int i = 1; i <= 100; i++)
{
string msg = $"test{i}";
SendNewMessage(msg);
}
}
private static void SendNewMessage(string message)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
Dictionary<string, object> args =
DeadLetterHelper.CreateDeadLetterQueue(channel,
"dl.exchange", "dead-letter", "DeadLetterQueue");
var result = channel.QueueDeclare("NewQueue", true, false, false, args);
Console.WriteLine(result);
channel.BasicPublish("", "NewQueue", null, Encoding.UTF8.GetBytes(message));
}
}
Now we have 100 messages:
Let’s simulate a server reboot:
Following the reboot, it’s gone:
Admittedly, that doesn’t sound very durable!
Why?
The reason for this, is that the durability of the queue doesn’t affect the durability of the message. At least, if the queue is durable, it doesn’t make the message so.
How can it be made persistent?
Let’s change our send code a little:
private static void SendNewMessage(string message)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
Dictionary<string, object> args =
DeadLetterHelper.CreateDeadLetterQueue(channel,
"dl.exchange", "dead-letter", "DeadLetterQueue");
var result = channel.QueueDeclare("NewQueue", true, false, false, args);
Console.WriteLine(result);
IBasicProperties prop = channel.CreateBasicProperties();
prop.Persistent = true;
channel.BasicPublish("", "NewQueue", prop, Encoding.UTF8.GetBytes(message));
}
}
The only difference is the addition of the IBasicProperties parameter. The Persistent flag is set. Now we’ll re run the same test; here’s the messages:
And after restarting the service:
As you can see, the messages are still there, and you can see the time difference where they’re been restored to the queue after a failure.
Speed - Introducing BenchmarkDotNet
I suppose the main question here is what price do you pay for durability. This gives me a chance to play with a new tool that I heard about a little while ago: BenchmarkDotNet.
It’s quite straightforward to use, just add the NuGet package:
Install-Package BenchmarkDotNet
There’s a bit of refactoring; I effectively ripped out the send and called it from a separate class:
class Program
{
static void Main(string[] args)
{
BenchmarkRunner.Run<SpeedTest>();
}
}
public class SpeedTest
{
[Benchmark]
public void SendNewMessagePersist()
{
MessageHelper helper = new MessageHelper();
helper.SendStringMessage("Test", "NewQueue", true);
}
[Benchmark]
public void SendNewMessageNonPersist()
{
MessageHelper helper = new MessageHelper();
helper.SendStringMessage("Test", "NewQueue", false);
}
}
I then ran this:
And it produced this:
So, it is a bit slower to persist the message. I’m not sure how helpful this information is: I probably could have guessed that persisting the message would have been slower beforehand. Having said that, I am quite impressed with BenchMarkDotNet!