his article looks at a line of business application built using .NET 2.0, Windows Communication Foundation, and Visual Studio 2005 that automatically chooses the most suitable connection based on the state of the user's network connection, providing reliability via message queuing on top of it.
Since network connectivity cannot always be guaranteed, what happens when the network goes down or a network connection is simply unavailable? How can you provide your users with the best connected experience regardless of the state of the network?
I'll start off with an overview and step-by-step configuration of a WCF service that exposes multiple bindings and wrap up with a pattern for adding logic to a Windows Forms test harness client that detects available network options and chooses the appropriate WCF binding at run time. Where the network is down or otherwise unavailable, I will provide the user with a reliable, "always on" computing experience using Queued Calls via Microsoft Message Queuing (MSMQ). As you will learn, MSMQ integrates seamlessly with WCF services. When network state is online, I will explore appropriate uses for TCP and HTTP depending on the geographical location of the user.
In addition to queued calls, WCF provides a number of powerful reliability features that are supported out of the box including WS-Reliable Messaging for managing message delivery even across multiple hops, and WS-Atomic Transactions for implementing distributed transactions. In this article, I will focus specifically on Queued Services, which are implemented seamlessly in WCF using Microsoft Message Queue (MSMQ). I will discuss these additional reliability features in future articles.
A Case for Queuing
In his excellent white paper, "The Rings of the Enterprise," Roger Sessions provided a brilliant analogy about how the design of good service-oriented applications is similar to the way Starbucks shops are run. I have since elaborated upon his analogy and used it time and time again as a metaphor to explain messaging communication patterns to developers and clients alike.
As I sit on a plane to Austin, TX having just finished my Starbucks "venti drip, room for cream"; the metaphor is fresh in my head.
Most Starbucks coffee shops have at least one register, a register clerk that handles the financial transactions, and a barista that makes the extravagant coffees that power corporate America today. I am not here to talk about how Starbucks has become an American icon (it has) or its influence on productivity in the workplace (which I believe is a fascinating topic), but what I have noticed is that in the most efficient locations, there is also a runner that is there to help with confections and non-ingredient-intensive beverages. I notice things like this because I am constantly applying programming problems to real-life situations since technology is merely a tool for solving real-world problems. As a result, it is no wonder that approaches to solving technical problems mirror real-life objects and interactions for which metaphors have been created. This is why not all problems can be solved with algorithms, and why sometimes, only heuristics will do the trick.
Next time you stand in line waiting to order your coffee—whether it is a "venti drip, room for cream (so I don't burn a hole in my pants)" or something a bit fancier like a Caramel Macchiato—consider what is happening in front of you:
- A customer places an order at the register.
- The customer pays for the items ordered.
- Next, depending on the contents of the order, the customer either waits for the items, or is asked for their name and implicitly, yet politely, asked to step aside and wait for the order to be completed.
In the last item above, something interesting happens. Based on the type of transaction, the items are delivered either in a classic "request-reply" pattern, where the customer waits momentarily for the response (i.e., the easy venti drip, room for cream, and zucchini walnut muffin), or steps aside waiting for the response which will consist of the customer's name being called when their Caramel Macchiato is ready.
The former is a synchronous call where the customer and the clerk block, or wait until the runner grabs the non-ingredient-intensive items. Sometimes, the clerk will even grab the items and serve them to you him or herself if the runner is too busy. The latter, however, is asynchronous, or "publish-subscribe." Once the financial transaction is complete, the clerk "publishes" the order with the crew behind the counter and uses the customer's name (a delegate) to correlate the notification (callback), hands off the work to either the runner (if baked goods or simple drip coffees are involved) or the barista (spawning new threads), and then is ready to service other calls (customers).
The customer subscribes to the order by giving the clerk his or her name. After the customer gives his or her name, what the customer does during this time is up to them. They can check their voicemail, check their meeting calendar, or prepare their packets of sugar, stirrer, and napkins. Heck, the customer can even use the restroom if needed depending on how many drinks the barista is working on ahead of the customer's order.
Now, have you ever been in a line at Starbucks that is so long that a nice employee comes out with a pad and a pen and actually starts taking orders before you reach the register? The best managed Starbucks stores do this. Do you know why? I think it is for two reasons. First, there is something to be said for proactive customer service, and when you are in a rush (as I almost always am), there is just something nice about the shared realization that the situation is unacceptable. Getting my order off my chest doesn't make the situation any more acceptable, but once I do, I can do other things like crack open my Windows Mobile phone to let my meeting organizer know that I am running late (again).
Second, there is also something more pragmatic about this approach. It is good business. You don't need a degree in Sociology or Psychology to sense the overwhelming anxiety that is prevalent in those occasional obscenely long lines, nor do you need an MBA to recognize that given the length of the line, losing the business of paying customers is a real possibility. People have places to go and people to see and, in that situation, Starbucks may not feel really confident that they will get each customer's order in before the customer reluctantly chooses to leave the store. In an effort to fulfill as many of the orders as possible given the long, volatile line, the nice employee cheerfully greets you and takes your order. You might not have your order fulfilled any faster, but you now have an assurance of your order being delivered. You also don't know if your credit card is going to be authorized or if the items you placed in your order are even available, but your message (written on the employee's note pad) is guaranteed to (eventually) reach the register.
In the example above, the long line represents the volatility of a network connection between endpoints. When the network is up and running, calls are being serviced efficiently, but sometimes, things go wrong. When they do, you must have a way to gracefully handle the condition. One way to manage the situation is to inform the customer that the call will be queued, which is essentially what the Starbucks employee is doing when he or she takes your order before you get to the register. Again, this is not an ideal situation, but it is far better than telling the customer that they are flat out of luck (or much worse, throwing an exception without any way of picking up the pieces).
Given this context, it is quite fun to pay closer attention to the interactions going on around you the next time you are at your local Starbucks. For example, I often like to analyze how adding another register (multiple instances as opposed to a single instance) or adding additional runners or baristas (threads) might impact the current experience. I'd be interested in hearing your perspectives too!
In this article, I will provide a practical example of how to build services in Windows Communication Foundation (WCF) that support queued calls. I will not cover documentation in depth, nor rehash technical details that are already covered extensively on MSDN and in books that are dedicated to WCF (see "Recommended Reading" in the sidebar).
To this end, I assume that you have a foundational understanding of WCF, including Service Contracts, Operation Contracts, Data Contracts, and basic binding fundamentals. If you are new to WCF, I highly recommend you start with a resource such as "WCF Essentials—A Developer's Primer," which will give you a kick start for the rest of this article.
|Editor's Note: This article was first published in the January/February 2008 issue of CoDe Magazine, and is reprinted here by permission.|