다음을 통해 공유


An Introduction to Service Bus Queues

In the new May CTP of Service Bus, we’re adding a brand-new set of cloud-based, message-oriented-middleware technologies including reliable message queuing and durable publish/subscribe messaging. We’ll walk through the full set of capabilities over a series of blog posts but I’m going to begin by focusing on the basic concepts of the message queuing feature. This post will explain the usefulness of queues and show how to write a simple queue-based application using Service Bus.

Let’s consider a scenario from the world of retail in which sales data from individual Point of Sale (POS) terminals needs to be routed to an inventory management system which uses that data to determine when stock needs to be replenished. I’m going to walk through a solution that uses Service Bus messaging for the communication between the terminals and the inventory management system as illustrated below:

Each POS terminal reports its sales data by sending messages to the DataCollectionQueue. These messages sit in this queue until they are received by the inventory management system. This pattern is often termed asynchronous messaging because the POS terminal doesn’t need to wait for a reply from the inventory management system to continue processing.

 

Why Queuing?

Before we look at the code required to set up this application, let’s consider the advantages of using a queue in this scenario rather than having the POS terminals talk directly (synchronously) to the inventory management system.

 

Temporal decoupling

With the asynchronous messaging pattern, producers and consumers need not be online at the same time. The messaging infrastructure reliably stores messages until the consuming party is ready to receive them. This allows the components of the distributed application to be disconnected, either voluntarily, e.g., for maintenance, or due to a component crash, without impacting the system as a whole. Furthermore, the consuming application may only need to come online during certain times of the day, for example, in this retail scenario, the inventory management system may only need to come online after the end of the business day.

 

Load leveling

In many applications system load varies over time whereas the processing time required for each unit of work is typically constant. Intermediating message producers and consumers with a queue means that the consuming application (the worker) only needs to be provisioned to accommodate average load rather than peak load. The depth of the queue will grow and contract as the incoming load varies. This directly saves money in terms of the amount of infrastructure required to service the application load.

 

 

Load balancing

As load increases, more worker processes can be added to read from the work queue. Each message is processed by only one of the worker processes. Furthermore, this pull-based load balancing allows for optimum utilization of the worker machines even if the worker machines differ in terms of processing power as they will pull messages at their own maximum rate. This pattern is often termed the competing consumer pattern.

Loose coupling

Using message queuing to intermediate between message producers and consumers provides an inherent loose coupling between the components. Since producers and consumers are not aware of each other, a consumer can be upgraded without having any effect on the producer. Furthermore, the messaging topology can evolve without impacting the existing endpoints – we’ll discuss this further in a later post when we talk about publish/subscribe.

 

Show me the Code

Alright, now let’s look at how to use Service Bus to build this application.

 

Signing up for a Service Bus account and subscription

Before you can begin working with the Service Bus, you’ll first need to sign-up for a Service Bus account within the Service Bus portal at https://portal.appfabriclabs.com/. You will be required to sign-in with a Windows Live ID (WLID), which will be associated with your Service Bus account. Once you’ve done that, you can create a new Service Bus Subscription. In the future, whenever you login with your WLID, you will have access to all of the Service Bus Subscriptions associated with your account.

 

Creating a namespace

Once you have a Subscription in place, you can create a new service namespace. You’ll need to give your new service namespace a unique name across all Service Bus accounts. Each service namespace acts as a container for a set of Service Bus entities. The screenshot below illustrates what this page looks like when creating the “ingham-blog” service namespace.

Further details regarding account setup and namespace creation can be found in the User Guide accompanying the May CTP release.


Using the Service Bus

To use the Service Bus namespace, an application needs to reference the AppFabric Service Bus DLLs, namely Microsoft.ServiceBus.dll and Microsoft.ServiceBus.Messaging.dll. You can find these in the SDK that can be downloaded from the CTP download page.

 

Creating the Queue

Management operations for Service Bus messaging entities (queues and publish/subscribe topics) are performed via the ServiceBusNamespaceClient which is constructed with the base address of the Service Bus namespace and the user credentials. The ServiceBusNamespaceClient provides methods to create, enumerate and delete messaging entities. The snippet below shows how the ServiceBusNamespaceClient is used to create the DataCollectionQueue.

Uri ServiceBusEnvironment.CreateServiceUri("sb", "ingham-blog", string.Empty);
string name = "owner";
string key = "abcdefghijklmopqrstuvwxyz";

    ServiceBusNamespaceClient namespaceClient = new ServiceBusNamespaceClient(
         baseAddress, TransportClientCredentialBase.CreateSharedSecretCredential(name, key) );

    namespaceClient.CreateQueue("DataCollectionQueue");

Note that there are overloads of the CreateQueue method that allow properties of the queue to be tuned, for example, to set the default time-to-live to be applied to messages sent to the queue.

 

Sending Messages to the Queue

For runtime operations on Service Bus entities, i.e., sending and receiving messages, an application first needs to create a MessagingFactory. The base address of the ServiceBus namespace and the user credentials are required.

 

    Uri ServiceBusEnvironment.CreateServiceUri("sb", "ingham-blog", string.Empty);
    string name = "owner";
    string key = "abcdefghijklmopqrstuvwxyz";

MessagingFactory factory = MessagingFactory.Create(
baseAddress, TransportClientCredentialBase.CreateSharedSecretCredential(name, key) );

From the factory, a QueueClient is created for the particular queue of interest, in our case, the DataCollectionQueue.

    QueueClient queueClient = factory.CreateQueueClient("DataCollectionQueue");

A MessageSender is created from the QueueClient to perform the send operations.

    MessageSender ms = queueClient.CreateSender();

Messages sent to, and received from, Service Bus queues are instances of the BrokeredMessage class which consists of a set of standard properties (such as Label and TimeToLive), a dictionary that is used to hold application properties, and a body of arbitrary application data. An application can set the body by passing in any serializable object into CreateMessage (the example below passes in a SalesData object representing the sales data from the POS terminal) which will use the DataContractSerializer to serialize the object. Alternatively, a System.IO.Stream can be provided.

 

    BrokeredMessage bm = BrokeredMessage.CreateMessage(salesData);

    bm.Label = "SalesReport";
    bm.Properties["StoreName"] = "Redmond";
    bm.Properties["MachineID"] = "POS_1";

    ms.Send(bm);

Receiving Messages from the Queue

Messages are received from the queue using a MessageReceiver which is also created from the QueueClient. MessageReceivers can work in two different modes, named ReceiveAndDelete and PeekLock. The mode is set when the MessageReceiver is created, as a parameter to the CreateReceiver operation.

 

When using the ReceiveAndDelete mode, receive is a single-shot operation, that is, when Service Bus receives the request, it marks the message as being consumed and returns it to the application. ReceiveAndDelete mode is the simplest model and works best for scenarios in which the application can tolerate not processing a message in the event of a failure. To understand this, consider a scenario in which the consumer issues the receive request and then crashes before processing it. Since Service Bus will have marked the message as being consumed then when the application restarts and begins consuming messages again, it will have missed the message that was consumed prior to the crash.

 

In PeekLock mode, receive becomes a two stage operation which makes it possible to support applications that cannot tolerate missing messages. When Service Bus receives the request, it finds the next message to be consumed, locks it to prevent other consumers receiving it, and then returns it to the application. After the application finishes processing the message (or stores it reliably for future processing), it completes the second stage of the receive process by calling Complete on the received message. When Service Bus sees the Complete, it will mark the message as being consumed.

 

Two other outcomes are possible. Firstly, if the application is unable to process the message for some reason then it can call Abandon on the received message (instead of Complete). This will cause Service Bus to unlock the message and make it available to be received again, either by the same consumer or by another completing consumer. Secondly, there is a timeout associated with the lock and if the application fails to process the message before the lock timeout expires (e.g., if the application crashes), then Service Bus will unlock the message and make it available to be received again.

 

One thing to note here is that in the event that the application crashes after processing the message but before the Complete request was issued then the message will be redelivered to the application when it restarts. This is often termed At Least Once processing, that is, each message will be processed at least once but in certain situations the same message may be redelivered. If the scenario cannot tolerate duplicate processing then additional logic is required in the application to detect duplicates which can be achieved based upon the MessageId property of the message which will remain constant across delivery attempts. This is termed Exactly Once processing.

 

Back to the code, the snippet below illustrates how a message can be received and processed using the PeekLock mode which is the default if no ReceiveMode is explicitly provided.

 

    MessageReceiver mr = queueClient.CreateReceiver();
    BrokeredMessage receivedMessage = mr.Receive();

    try
    {
        ProcessMessage(receivedMessage);
        receivedMessage.Complete();
    }
    catch (Exception e)
    {
        receivedMessage.Abandon();
    }

Wrapping up and request for feedback

Hopefully this post has shown you how to get started with the queuing feature being introduced in the new May CTP of Service Bus. We’ve only really just scratched the surface here; we’ll go in to more depth in future posts.

 

Finally, remember one of the main goals of our CTP release is to get feedback on the service. We’re interested to hear what you think of the Service Bus messaging features.

 

We’re particularly keen to get your opinion of the API, for example, do you think it makes sense to have PeekLock be the default mode for receivers? We have a survey for that question.

 

For other suggestions, critique, praise, or questions, please let us know at the AppFabric CTP forum. Your feedback will help us improve the service for you and other users like you.

Comments

  • Anonymous
    August 02, 2011
    Outstanding article. For first contact with Service Bus Queues - perfect! All basic and necessary things to start with queues were clearly explained. Great job.