I have below a basic wrapper around the Azure Service Bus code that we will be using within a worker role. This ServiceBusClient
will be instantiated each time the worker role is run; then used to access the queue until there are no remaining items left to enumerate through.
public class ServiceBusClient : IDisposable, IServiceBusClient
{
private const int DEFAULT_WAIT_TIME_IN_SECONDS = 120;
private const string SERVICE_BUS_CONNECTION_STRING_KEY = "service.bus.connection.string";
private readonly MessagingFactory _messagingFactory;
private readonly NamespaceManager _namespaceManager;
private readonly QueueClient _queueClient;
private readonly ISettingsManager _settingsManager;
public ServiceBusClient(ISettingsManager settingsManager, string queueName)
{
_settingsManager = settingsManager;
var connectionString = _settingsManager.GetSetting<string>(SERVICE_BUS_CONNECTION_STRING_KEY);
_namespaceManager = NamespaceManager.CreateFromConnectionString(connectionString);
_messagingFactory = MessagingFactory.CreateFromConnectionString(connectionString);
_queueClient = GetOrCreateQueue(queueName);
}
public void Dispose()
{
_messagingFactory.Close();
}
public BrokeredMessage ReceiveTopMessage()
{
return _queueClient.Receive(TimeSpan.FromSeconds(DEFAULT_WAIT_TIME_IN_SECONDS));
}
public void SendMessage(object bodyObject)
{
var message = new BrokeredMessage(bodyObject);
_queueClient.Send(message);
}
private QueueClient GetOrCreateQueue(string queueName)
{
var queue = !_namespaceManager.QueueExists(queueName)
? _namespaceManager.CreateQueue(queueName)
: _namespaceManager.GetQueue(queueName);
return _messagingFactory.CreateQueueClient(queue.Path, ReceiveMode.PeekLock);
}
}
As you can see I initialise the NamespaceManager
, MessagingFactory
and QueueClient
inside the constructor: they are then reused when calling SendMessage()
and ReceiveTopMessage()
with the connection closed using a Dispose()
method.
My question is whether the approach I am using is safe; will keeping a single instance of the QueueClient
open whilst a worker role enumerates through all the messages on the queue (A process which could keep the connection open for quite a while with a long wait between calls to ReceiveTopMessage()
) work consistently without transient issues, or is it prudent to open and close the connection each time?
As an aside; how is the transient fault handling conducted in the Microsoft Service Bus code? Is it conducted by default or do we need to implement the Transient Fault Handling Framework?
The QueueClient
class uses a connection that is managed by the MessagingFactory object used to create it. It is recommended to reuse the same client object for multiple requests. As documented in Best Practices for Performance Improvements Using Service Bus Brokered Messaging:
The Service Bus enables clients to send and receive messages via two protocols: the Service Bus client protocol, and HTTP. The Service Bus client protocol is more efficient, because it maintains the connection to the Service Bus service as long as the message factory exists. It also implements batching and prefetching. The Service Bus client protocol is available for .NET applications using the .NET managed API. (...) Service Bus client objects, such as QueueClient or MessageSender, are created through a MessagingFactory object, which also provides internal management of connections. You should not close messaging factories or queue, topic, and subscription clients after you send a message, and then re-create them when you send the next message. Closing a messaging factory deletes the connection to the Service Bus service, and a new connection is established when recreating the factory. Establishing a connection is an expensive operation that can be avoided by re-using the same factory and client objects for multiple operations. (...) All clients (senders in addition to receivers) that are created by the same factory share one TCP connection.
Regarding transient fault handling, QueueClient
has a RetryPolicy property that that determines whether the request should be retried. There is also the Transient Fault Handling Application Block, which supersedes the Transient Fault Handling Framework.
Regarding message receive loops, there is guidance in Implementing Reliable Message Receive Loops. Microsoft has recognized that it's hard to implement a well-writen, resilient message receive loop, so they have introduced the Event-Driven Message Programing Model as an alternative.