.NET Services March 2009 CTP - Service Bus Routers and Queues - Part 5: The Queue API for the rest of us
In Parts 3 and 4 of this series I’ve explained the REST protocol for the .NET Service Bus Queue capability. If that looked a little too complicated for your taste and you’d rather clean with a simple API surface, here’s the API that you’ll prefer.
We’ve got two simple classes in the SDK (in the Microsoft.ServiceBus assembly/namespace) that do the job. The QueueManagementClient allows you to create, renew and delete queues in/from the .NET Service Bus namespace:
1: public static class QueueManagementClient2: {3: public static QueueClient CreateQueue(TransportClientEndpointBehavior credential,4: Uri queueUri, QueuePolicy queuePolicy);5: public static void DeleteQueue(TransportClientEndpointBehavior credential,6: Uri queueUri);7: public static QueueClient GetQueue(TransportClientEndpointBehavior credential,8: Uri queueUri);9: public static QueuePolicy GetQueuePolicy(TransportClientEndpointBehavior credential,10: Uri queueUri);11: public static DateTime RenewQueue(TransportClientEndpointBehavior credential,12: Uri queueUri, TimeSpan requestedExpiration);13: }
The most striking difference between this API (and the underlying SOAP protocol for which we still owe you some docs) and the REST API/Protocol is that there’s only a single URI to deal with. The WS-Transfer aligned protocol to manage the policy, the “Queue Transfer” protocol used to dequeue messages, and the “Enqueue” protocol to add messages to the queue are all overlaid over the exact same URI.
The only somewhat ugly element here is the TransportClientEndpointBehavior that’s our awkwardly named credentials container. That class was meant to evolve into something else and then we changed our mind at some point, which is how the baby got stuck with that name. We’ll give that a prettier moniker in one of the next CTPs and as part of an overhaul of the .NET Access Control integration into Service Bus.
The function of the methods themselves should be quite obvious. You can create a queue by applying a policy, you can delete a queue, attach to an existing queue, get the latest queue policy, and renew (extend the lifetime) of a queue. For completeness, here’s CreateQueue in context:
1: Uri queueUri = ServiceBusEnvironment.CreateServiceUri("sb", solutionName, "/MyQueue/");2:3: QueuePolicy queuePolicy = new QueuePolicy();
4: queuePolicy.ExpirationInstant = DateTime.UtcNow + TimeSpan.FromHours(1);5:6: QueueClient client = QueueManagementClient.CreateQueue(credential, queueUri, queuePolicy);7: queuePolicy = client.GetPolicy(); // get effective policy
The QueueClient class allows interaction with a Queue. QueueClient instances cannot be created directly, but must be created via the QueueManagementClient factory.
1: public sealed class QueueClient2: {3: public void DeleteLockedMessage(Message message);4: public void DeleteQueue();5: public DateTime GetExpiration();
6: public QueuePolicy GetPolicy();
7: public Message PeekLock();
8: public Message PeekLock(TimeSpan timeout);
9: public IEnumerable<Message> PeekLockMultiple(int maxMessages);10: public IEnumerable<Message> PeekLockMultiple(int maxMessages, TimeSpan timeout);11: public void Purge();12: public void ReleaseLock(Message message);13: public DateTime Renew(TimeSpan requestedExpiration);
14: public Message Retrieve();
15: public Message Retrieve(TimeSpan timeout);
16: public IEnumerable<Message> RetrieveMultiple(int maxMessages);17: public IEnumerable<Message> RetrieveMultiple(int maxMessages, TimeSpan timeout);18: public void Send(Message message, TimeSpan timeout);19: public RouterSubscriptionClient SubscribeToRouter(RouterClient routerClient, TimeSpan requestedTimeout);
20: }
As you can tell, the class is – well – a queue client. It’s using WCF’s Message class as its message abstraction and supports sending messages into the queue, reading messages off the queue in a destructive fashion (Retrieve), and reading messages off the queue using the Peek/Lock pattern which provides resilience against message loss if the message were to be lost in transfer or the receiver fumbled the message. What you’ll also notice is that there are RetrieveMultiple and PeekLockMultiple variants of the retrieval functions which allow for getting more data with fewer network roundtrips.
Again, there should be no surprises using the API. Here’s how you send:
1: queueClient.Send(Message.CreateMessage(MessageVersion.Default, "Hello", input), TimeSpan.MaxValue);
and here’s how you do a destructive read:
1: Message message = queueClient.Retrieve();
and here’s how you use the Peek/Lock pattern:
1: Message message = queueClient.PeekLock();2: string content = message.GetBody<string>();3: queueClient.DeleteLockedMessage(message);
There are several examples in the SDK showing how to use the API for Queues. The OnewayQueueSender and SoapHttpQueueSender samples are particularly interesting since they just use WCF channels to enqueue, including all the bells and whistles you get from using a WCF channel. Here’s how the OnewayQueueSender does it:
1: ChannelFactory<IOnewayChannel> channelFactory =2: new ChannelFactory<IOnewayChannel>(new NetOnewayRelayBinding(),3: new EndpointAddress(queueUri));
4: channelFactory.Endpoint.Behaviors.Add(userNamePasswordServiceBusCredential);5: IOnewayChannel onewayChannel = channelFactory.CreateChannel();6:7: string input = Console.ReadLine();
8: onewayChannel.Hello(new HelloMessage(input));
The logical next question is: So why is the Queue not hooked up to a WCF listener? Answer: That’s what we’ve got “buffered Routers” for. The queue very explicitly provides a ‘pull’ model and the WCF listener would abstract that away and turn it into ‘push’. Routers provide ‘push’ natively and they can embed a Queue. More on Routers in the next set of posts in this series.