Friday, 26 October 2007

Communications for ajax apps

What are the options?
Communication with server could be done with a java applet, or with flash. However, both of these are a little kludgy and assume the user has those plugins. It can also be done with AJAX, which often makes for a more streamlined solution.
XMLHttpRequestUnless you've been hiding under a rock for the last couple of years, you'll have heard of AJAX - the latest in a long line of buzz words. AJAX covers a number of technologies - Javascript, DOM manipulation, XML, and XMLHttpRequest.
The last one of these is a powerful beast. It allows Javascript code to call back to the server to request more data. This is used for all sorts of new interactive webapps that mean the end user doesn't have to wait for page reloads every time they do something.
Essentially using the XMLHttpRequest means that you can break out of the request/response paradigm of the web, and write webapps that function much more like desktop applications.
The basic usage of an XMLHttpRequest is to perform an HTTP operation with the server (GET/POST/etc), so the client sends a request to the server, and receives a response back.
But how much further can we push the XMLHttpRequest object? Doing a request/response is it a bit limited. Why the hell didn't they just allow us to open a raw socket to the server and have javascript functions send() recv() close() etc. I don't want http. I don't want xml. Give me a raw socket and I'm happy. Fact is they didn't do that, so we'll have to work around it.
First lets take an example. We're writing a simple chat application.
This requires data to be sent in both directions. When someone else says something, the server needs to 'send' that message to the client. When the client says something, it has to 'tell' the server about it. This doesn't instantly fit into the HTTP protocol request/response idea.
Polling
First method people would probably use would be 'polling'. With this you would periodically ask the server if it has any new messages for you. You could either send messages you have in these poll requests, or in a separate ajax call if you want them to be instant.
This is ok, but an utter waste of resources and bandwidth.
Lazy polling
A better method is to use lazy polling. Here, you ask the server if it has any new messages for you, and the server holds your connection open until either it does, or a timeout expires. Then you immediately ask it again.
Advantage: You get the messages as soon as you should. Unlike polling, where you may get a message late, with lazy polling, you pretty much get messages as soon as the server has them for you. This makes your webapp far more responsive.
Advantage: Far less requests, so less HTTP headers etc. Instead of polling the server, maybe sending a request every second, you can just send 1 request, and wait until a timeout say 60 seconds later.
Disadvantage: Your webserver has a connection held open to every client using the app. Depends how well your webserver is configured/programmed to handle this one. Connections are cheap, so it's not really an issue.
But how do you send data to the server? Well it turns out that most common web browsers all you 2 concurrent requests to any one domain at the same time. So that means we can have one connection doing lazy polling for receives, and when we need to send a message (or request an image or other), we can use a separate 'send' ajax request. Note that for further efficiency this request can also respond with messages from the server.
For example, if you have your lazy polling request going, then you send a chat message on your other 'send' request, and the server wants to reply with something, or perhaps coincidently someone else has just said something at exactly the same time, it makes more sense for the server to send these messages down the 'send' request which is open, rather than send them down the 'recv' lazy polling request. If it did that, the client would have to start a new lazy poll request which would waste a bit of bandwidth.
Keep-Alive
Any more optimisations? Well certainly configure to use keep-alive. Keep-alive allows the client to stay connected to the server and simply send more requests on the open connection.
In this configuration, the webapp maintains 2 connections to the server. 1 is the lazy polling 'recv' connection, used to enable the server to 'push out' messages to the client.
The other connection is used for sending data to the server, receiving messages if applicable, and also to fetch other media/images/etc if needed (Although it may be a good idea to put other media on a separate subdomain to allow it to run in its own connection).
HTTP headers?
I have a gripe with http headers... If I open a keep-alive connection to a server, and send 10 requests down that connection, it doesn't make sense to me to send all headers 10 times. The server already knows my userAgent from the first time I said it.
For the other direction we get a little more say, so we can minimise the headers we send out from the server to the client.
How much we talking? Well, it's not good... for my IRC webapp, 84% of received traffic is HTTP headers. 42% of sent traffic is HTTP headers.
However, it's a small amount of data in the grand scale of things, and is the best that's possible.
So although the XMLHttpRequest is pretty lame compared to a raw socket, it can be used exactly like a raw socket if you configure it right.
Using this type of 'raw socket' emulation turns out to give pretty good transfer rates for data, as well as 'ping' times.