Asynchrony: There Is No Spoon
Monday, 19 April 2004
One of the things that people find compelling about Web services is its promise of asynchrony. “HTTP is only request/response, and therefore synchronous; it’s terrible for long-lived business processes, where the server needs to contact the client at some arbitrary time in the future” they say.
A single HTTP request/response is indeed synchronous, so that that level this argument holds. In the common case, one party (the server) can’t send messages to the other (the client) without receiving a corresponding request. However, there are a couple of easy ways to work around this.
The simplest is polling the server; just keep on GETting the URI. This is how RSS works, for example; the client knows (through application, or really format-specifc semantics) to check on the server for an update every so often. This has the nice property of not requiring the server to know the client’s address, or even be able to connect to it; the client takes care of that. On the down side, it isn’t immediate; unless the client polls continuously (consuming considerable resources), it won’t know of an update for a little while. Still, in many situations where instantaneous messaging isn’t necessary, polling is good enough.
Another technique is for the server and client to conspire to hold the underlying TCP connection open as long as possible, so that the server can send the response when it’s ready to. KnowNow and mod_pubsub take this approach; while it consumes some resources on both sides, it scales reasonably well, thanks to techniques like event looping (which allows tens of thousands of concurrent TCP connections to be held open indefinitely, with practically no overhead). It also has the benefits that we saw with polling, regarding the server not needing to have explicit addressing information about the client. This is a good intermediate approach.
A final approach would be to give the client its own explicit address (http://me.example.org/), modelling it as a resource as well. In effect, it becomes both a server and client at the same time. This pushes asynchrony to the application itself, by surfacing the different parties as different resources; this gives a lot of flexibility and power to application designers, but requires the client to be reachable by the server; a difficult task when it’s mobile or behind a firewall. An example of this approach can be seen in my WARM proposal to the now-defunct WEBI IETF Working Group.
You Say Asynchronous, I Say Pattern of Use
The point here is that calling a wire protocol “synchronous” is misleading. As we see above, you achieve asynchrony by interacting in a particular pattern; which is determined more by your application design and the abstractions provided by your tools than it is by the wire protocols you use.
Put another way, every wire protocol needs to identify the recipient of a message; the only difference is how your tools present this choice to you. Polling or keeping a connection open hides it in the details of TCP; minting a new resource pushes it into the architecture of your application. The choice is one of abstraction; remember that while they’re layered in nice stacks in our heads and (sometimes) our software, on the wire all of this information is jumbled up together.
Indeed, most “asynchronous messaging protocols” actually break down into smaller, synchronous interactions; SMTP is commonly thought of as asynchronous, but is actually very synchronous “on the wire.” Furthermore, most e-mail is read through a polling interface (POP or IMAP). All of this synchronous behaviour is abstracted out to a one-way message, as far as users are concerned, but that’s only because their software has chosen to hide this from them (mostly; how many times do you click “get new mail” in a day?).
Choosing The Right Abstraction
The Web services stack is creating such an abstraction on top of HTTP, by defining patterns of use that allow you to achieve asynchrony, reliability and other features. As I’ve said before, HTTP can already provide reliability and high performance, and as we see here, asynchrony. What are the differences between SOAP and HTTP, then?
The obvious difference is the abstraction presented, and where the functionality is provided in relation to it. Web Services pushes things like reliability and asynchrony down into the details of the protocol, so that the developer can flip a switch and have these features; the abstract shape of the service (i.e., the portType) doesn’t change.
The abstraction that HTTP provides, on the other hand, requires the application to be architected to exhibit “real” asynchrony; you have to model it as an update of state to a separate resource. The same is true if you want to enable reliability.
Here’s the paradox; Web services (as most people see it) provides a simple messaging abstraction, and provides a lot of “built-in” functionality of the switch-throwing variety. After all, WSDL has more layers of abstraction than you can throw a stick at, and each one can hide more than the last. On the other hand, HTTP* requires the developer to take more responsibility in their application design, but in doing so, also gives a higher-level, and potentially more useful, abstraction to work with; stateful resources.
Which is better? It depends on what you’re looking for. Maybe I’ve just got a hammer, but it seems to me that one of the impediments to people constructing HTTP applications in patterns like this is the lack of a good way to describe them and attach metadata to them.
- As it stands today; there have been proposals to add things like reliable POST with a new, generic method.