Saturday, 6 November 2010

AspComet - High Level Architecture Overview

I have received a few requests to give one of these, and I promised over a year ago that I would do! Being a typical developer I don't like writing documentation, but I think this will help with anyone doing any work with AspComet to understand what's going on under the hood.

I assume you have basic knowledge of the Bayeux protocol before reading this document. If not, then give it a read as it will help to understand what I’m talking about!

Bayeux “Clients”

When a browser first connects to AspComet it will enter a series of negotiation requests. The server is at this point creating a Client object to represent that browser and giving it a unique ID, which is communicated back to the browser and understood and stored by the Bayeux JavaScript implementation. This client ID is used in all subsequent requests between browser and server to marry up the requests.

On the server side, the client class stores, amongst other things, the list of channels to which the browser has subscribed, such that it can send the appropriate messages to that browser when required.

Async Http Handlers

As I mentioned in a previous blog post, we implement async http handler which is provided by the ASP.NET pipeline. These handlers are designed for sites which perform long running operations and wish to avoid starving the ASP.NET thread pool during these operations (by returning threads back to ASP.NET during the time that said operations are being made). AspComet piggybacks off of this and uses it in a slightly different way as probably intended.

When a request comes into the browser, it’s picked up by the BeginProcessRequest method where we create an AsyncResult to return back to ASP.NET. At this point, the thread will be returned to the request pool.

Two things can now happen here – if there is data already there to be sent to the browser (or the incoming messages generated responses which need to be sent straight back) then the AsyncResult will be completed immediately with those messages (raising the callback which ASP.NET gave us to call when we’re ready). Otherwise the AsyncResult gets stored along with the client and will be used at the point a message is ready to be sent.

To illustrate this with an example, client A wants to send a message to client B. Client B connects, and has no pending messages, so his AsyncResult is stored inside his Client instance. At this point client A’s browser sits there waiting for a response to come back. Client A sends a publish message which needs to be sent to B, at that point from within Client A’s handler thread Client B’s callback is called, sending the message straight to Client B’s browser.

Message Handlers

Within Bayeux there are a number of predefined channels which are used for passing specific requests to the server. These are called Meta channels. Within AspComet there is a Message Handler for each one.

The HttpHandler receives an array of messages from the browser and passes them all to the MessageBus. This splits up the array and passes each message to the appropriate handler in turn. Once all messages have been handled, it’s up to the message bus to decide whether to complete the AsyncResult immediately or store it in the client to be called at a later date. I won’t go into detail about each MessageHandler is doing, as it’s basically here that the Bayeux protocol implementation comes into play – the protocol is essentially providing a spec for what each of the handlers should be doing.

Application Interfaces

The primary way for applications to interface with AspComet is via Events and the EventHub. Events are raised by the message handlers for each type of message being handled, which go through the central EventHub static class. This enables pretty much any scenario you can think of!

Applications will typically want to be notified when messages are published to specific channels, when new clients connect and when clients subscribe and unsubscribe to channels. This is all supported via Events. Some events also support cancellation, so an application could subscrivbe to those and set Cancel=True, for example to cancel subscription or block certain messages reaching channels.

For complete control, it’s also possible to use dependency injection to inject alternate implementations of some of the types used by AspComet.

The best place to learn is to look at the Chat sample which comes bundled with AspComet. That shows how to use the Dojo and JQuery JavaScript Comet frameworks to interface with AspComet, and has some good (but contrived) examples of subscribing to events and injecting alternate dependencies.