Instant Update
When you try Meebo.com with your desktop messenger application you will see that Meebo.com updates your messages more or less instantly.
The updates are so fast that there could never be a time controlled javascript pinging the web server. There must be another way of doing continuous updates with ajax.
Delayed Server Response
When I watched the http traffic between my browser and Meebo.com I realized that Meebo uses some kind of delayed server response technology. This must be handled in the following way (more or less):
- The browser requests new events from the server by sending a XmlHTTPRequest (plain old ajax)
- then the server accepts the request but instead of sending an immediate response it rather waits for any (external) event or a controlled timeout (after 30 seconds):
- if there was an event e.g. a message was delivered to the server, the server sends a response to the client telling the client the news about the event, or
- if there was no event, the server “times out” after — let’s say 30 seconds — and sends a no-events-happened response to the client.
- after that steps, the client takes the response and processes it (prints the message, updates some properties, et cetera)
- after the last step (or even better before?) a new XmlHTTPReqeuest will be sent and the process begins from the very start …
I have done a basic implemenation of this algorithm as a proof of concept for zope2.
Proof of Concept
Index_html File
This file displays the events and calls via Ajax some server side functions.
Create an index_html page template in Zope with the following content:
<html><head> <title tal:content="template/title">The title</title>
<script type="text/javascript">
function getDescription() { var url = 'events';
if (window.XMLHttpRequest) { req = new XMLHttpRequest(); } else if (window.ActiveXObject) { req = new ActiveXObject("Microsoft.XMLHTTP"); } printEvent('Client','Sent request ...'); req.onreadystatechange = processRequest; req.open("GET", url, true); req.send(null); }
function processRequest() { if (req.readyState == 4) { if (req.status == 200) { printEvent('Client','Got response ...'); parseMessages(); getDescription(); } else { printEvent('Client','Not able to retrieve response'); } } }
function parseMessages() { response = req.responseText; if (response == 'None') { return; } printEvent('Server',response); }
function printEvent(who,text) { printDiv = document.getElementById('printDiv'); divtext = printDiv.innerHTML; printDiv.innerHTML = divtext + '<br/><'+'b>'+ who +': <'+'/b'+'>' + text }
</script></head>
<body onload="getDescription()"> <h2><span tal:replace="here/title_or_id">content title or id</span></h2> <div id="printDiv">Events ...</div></body></html>
Then we need a Python script called by the Javascript in the index_html file above:
Python Script ‘events’
This Python script simulates random events. In the variable myRandomEvent a random number between 0 and 20000 represents the time when the events should be fired. The server response waits for a maximum of 10 seconds (waitForMillis).
## Script (Python) "events"##bind container=container##bind context=context##bind namespace=##bind script=script##bind subpath=traverse_subpath##parameters=##title=##from random import random waitForMillis = 10000myRandomEvent = random()*waitForMillis*2 startTime = DateTime().millis() while 1: now = DateTime().millis() elapsedTime = now - startTime if elapsedTime >= waitForMillis: return "Not fired, no event" if elapsedTime >= myRandomEvent: return " After " + str(elapsedTime) + " millis - event fired!" return "None"
Sample Output
Events ...Client: Sent request ...Client: Got response ...Server: After 6543 millis - event fired!Client: Sent request ...Client: Got response ...Server: After 5028 millis - event fired!Client: Sent request ...Client: Got response ...Server: After 1788 millis - event fired!Client: Sent request ...Client: Got response ...Server: After 6666 millis - event fired!Client: Sent request ...Client: Got response ...Server: Not fired, no eventClient: Sent request ...Client: Got response ...Server: After 8848 millis - event fired!Client: Sent request ...Client: Got response ...Server: Not fired, no event
Client: Sent request ...Client: Got response ...Server: After 9813 millis - event fired!Client: Sent request ...
This example is very easy and works as intended. When you change the value for myRandomEvent to myRandomEvent = random()*waitForMillis*.2 in the Python script to fire events more quickly, you’ll get something like this:
Events ...Client: Sent request ...Client: Got response ...Server: After 385 millis - event fired!Client: Sent request ...Client: Got response ...Server: After 244 millis - event fired!Client: Sent request ...Client: Got response ...Server: After 1659 millis - event fired!Client: Sent request ...Client: Got response ...Server: After 480 millis - event fired!Client: Sent request ...Client: Got response ...Server: After 1496 millis - event fired!Client: Sent request ...Client: Got response ...Server: After 1742 millis - event fired!Client: Sent request ...Client: Got response ...Server: After 494 millis - event fired!Client: Sent request ...Client: Got response ...Server: After 1945 millis - event fired!
Problems
There are some side-effects for this on the server side. I don’t know how a default Apache web server is behaving when leaving open a fairly large amount of http requests for such a long time. Some of this problems are addressed here. Maybe it’s better not take a standard web server like Apache than do one’s own implementation of a http server which can handle this large amount of long time http connections? I think this also holds true for Zope, which, if I remember correctly, only can handle about 5-7 requests concurrently.
Update:Â Alex Russel recently coined the term comet for this kind of technology. I recommend reading his article (Comet: Low Latency Data for the Browser), he describes my findings much better than I did here
!











