Socket Proliferation

July 5th, 2008

Since discussing browser sockets,, I’ve been doing some research on other implementations. For some time now, David Davis has had a browser socket with his Sprocket.Socket implementation.

I must give David credit for being so forward thinking, being the first to release a browser socket. With the Orbited project, we had to undergo a lengthy process to arrive at the same conclusion he had already found. We realized that we wanted reliable delivery with message ordering guarantees, so we created a protocol that we called Comet Session Protocol (CSP). Next, we started working on an API for CSP, and decided that we might as well use HTML5’s then-named TCPConnection API. We worked with that for a while, and then realized that we weren’t actually honoring the API because we didn’t implement the server-side protocol (TCPConnection then, like WebSocket now, had an extra handshake for security, and framing.) So we went and implemented that, only to realize that we didn’t really need the TCPConnection security provided by the handshake — Orbited could provide it instead via access control! So we created a TCPSocket API which simulates a raw socket in the browser.

I must say — I am extremely impressed with David’s clear understanding of the problem domain. It took the entire Orbited core developer team a few months to follow this line of reasoning and end up where we are now.

Also of note, JP Calderone put together an alternative implementaiton using Twisted Athena, and I imagine more will soon follow. This is great news. Having multiple browser socket implementations is exactly what we need. This way, users can experiment with the various servers and choose whichever works best for them. It leaves us free to move up the stack — That is, we can begin implementing protocols on top of our basic API. We should make sure to all choose the same basic API so that any protocol implementation can actually be used with any server.

For now, the Orbited project will continue protocol work with the TCPSocket interface. I think its important to keep as close to the WebSocket API as possible. Of course, I’m open to discussion from anyone interested in joining forces or providing some advice.

New Tutorial: How to Write an IRC Client in JavaScript

July 2nd, 2008

There is a new tutorial for Orbited 0.5 explaining how to write an IRC client with no server-side code! This is a good introduction to using TCPSocket for implementing clients for other protocols.

Shapes on a Plane

July 1st, 2008

I’ve been making various little canvas graphics for an upcoming browser-based pure-JavaScript real-time multiplayer game, and decided to implement some shape primitives. Specifically, I created functions for drawing circles and regular polygons. Then, I decided I could use some curvy and straight stars, and, based on the regular polygon code, made two more functions, regularQuadraticStar and regularStar. Here’s an example of 50 of these shapes drawn on a canvas:

First, and most simply, a circle function. This one is not too much easier than just using the «context».arc function directly, but it makes code a tiny bit clearer, and also saves a couple of lines of code for each circle:

var circle = function (context, x, y, radius) {
  var c = context;
  c.beginPath();
  c.arc(x,y,radius,0,Math.PI*2, true)
  c.closePath();
}

Next, regular polygons can be easily drawn by translating, scaling, and rotating our drawing context, and then repeatedly drawing to the point (1, 0) and rotating the canvas. This is much easier to code and more readable than calculating the endpoints of a regular polygon. We remember to save the graphics context at the beginning of the function and restore it at the end:

var regularPolygon = function (context, x, y, radius,
                               angle, sides) {
  var c = context;
  var inner_angle = (2.0 * Math.PI) / sides
  c.save();
  c.beginPath();
  c.translate(x,y);
  c.rotate(angle);
  c.scale(radius, radius);
  c.moveTo(1,0);
  for (var i=0; i < sides; i++) {
    c.rotate(inner_angle);
    c.lineTo(1,0);
  };
  c.closePath();
  c.restore();
};

After drawing many pretty regular polygons, I decided that it might be nice to draw stars with the same corners as those regular polygons. To that end, I first made a regularQuadraticStar function which behaves identically, but adds a control point at (0, 0) between each pair of vertices:

var regularQuadraticStar = function (context, x, y, radius,
                                     angle, sides) {
  var c = context;
  var inner_angle = (2.0 * Math.PI) / sides
  c.save();
  c.beginPath();
  c.translate(x,y);
  c.rotate(angle);
  c.scale(radius, radius);
  c.moveTo(1,0);
  for (var i=0; i < sides; i++) {
    c.rotate(inner_angle);
    ctx.quadraticCurveTo(0,0,1,0);
  };
  c.closePath();
  c.restore();
};

And then, because sometimes normal pointy stars are needed, I made a regularStar function, which takes arguments like those of regularPolygon, along with one more, the “pointiness” of the star, which ranges from 0 (not at all pointy) to 1 (basically just radial lines or spokes):

var regularStar = function (context, x, y, radius,
                            angle, sides, pointiness) {
  var c = context;
  var half_inner_angle = (Math.PI) / sides
  c.save();
  c.beginPath();
  c.translate(x,y);
  c.rotate(angle);
  c.scale(radius, radius);
  c.moveTo(1,0);
  for (var i=0; i < sides; i++) {
    c.rotate(half_inner_angle);
    ctx.lineTo(1 - pointiness, 0);
    c.rotate(half_inner_angle);
    ctx.lineTo(1,0);
  };
  c.closePath();
  c.restore();
};

And then, to test the whole thing, I decided to generate a number of random shapes and colors, and then draw them on a canvas.

To do that, I needed a couple of simple helper functions, based on Math.random():

var uniform_random = function (low, high) {
  if (typeof(high) === 'undefined') {
    high = low;
    low = 0;
  }
  return low + (high - low) * Math.random()
};

var random_int = function (low, high) {
  return Math.floor(uniform_random(low, high))
}

var random_choice = function (array) {
  var choice = Math.floor(Math.random() * array.length);
  return array[choice];
};

var random_color = function() {
  var r = random_int(30, 256).toString();
  var g = random_int(30, 256).toString();
  var b = random_int(30, 256).toString();
  var a = uniform_random(0.2, 1.0).toString();
  return ‘rgba(’ + r + ‘, ‘ + g + ‘, ‘ + b + ‘, ‘ + a + ‘)’
}

And finally, using all of these functions, it was possible to make a function to create a random shape:

var randomShape = function (context, width, height, maxradius) {
  context.save()

  var minradius = 0.1 * maxradius;
  var minstroke = 0.5;
  var maxstroke = 5.0;

  var x = uniform_random(0, width);
  var y = uniform_random(0, height);
  var radius = uniform_random(minradius, maxradius);
  var angle = uniform_random(0.0, 2.0 * Math.PI);
  var sides = random_int(3,9);
  var pointiness = uniform_random(0.3, 0.9);

  var shape = random_choice([circle, regularPolygon,
                             regularQuadraticStar, regularStar]);

  context.fillStyle = random_color();
  context.strokeStyle = random_color();
  context.lineJoin = random_choice(['round', 'bevel', 'miter']);
  context.miterLimit = 10;
  context.lineWidth = uniform_random(minstroke, maxstroke);

  // actually draw the shape
  shape(context, x, y, radius, angle, sides, pointiness);
  context.fill();
  context.stroke();

  context.restore()
};

And then finally create a number of random shapes:

var canvasElement = document.getElementById('shapes');
canvasElement.width = 400;
canvasElement.height = 250;
var ctx = canvasElement.getContext('2d');

// fill our canvas with black
ctx.save();
ctx.fillStyle = 'rgb(0, 0, 0)';
ctx.fillRect(0,0,400,250);
ctx.restore();

for (var i=0; i < 50; i++) {
  randomShape(ctx, 400, 250, 30);
};

And there we have it—a canvas filled with 50 random shapes:

Orbited 0.5.0

June 22nd, 2008

You may have noticed the lack of a 0.4.x release. We tried to make a number of improvements for 0.4.x, but ultimately we never thought that branch was stable enough to release. The 0.5.0 release, on the other hand, is the most stable and feature-rich version of Orbited yet. This is a pre-announcement that should give you a day or two notice and some information about porting your app to 0.5. You can expect the official release announcement over the next couple of days.

While we still support the old orbited protocol, there are a couple of differences when using Orbited 0.5.x with 0.3.x applications:

  • orbited.js still contains an Orbited.connect function, but this function now may takes only two arguments: event_cb and token. Orbited connections are now identified by a single token string. If you provide multiple arguments, the javascript client will simply concatanate them with commas in between.
  • when you use an orbit client to send data from your web application, the list of recipients should just be strings that exactly match the token used from the javascript
  • There is no proxy. Instead, Orbited.connect will just work in all browsers cross-port and cross-subdomain.
  • You need to specify where the orbited server is via javascript globals. ORBITED_DOMAIN = ‘127.0.0.1′; ORBITED_PORT = 8000; for example.

Porting an application from the previous version to the new one shouldn’t be hard. They should work out of the box after changes the include from /_/static/orbited.js to /static/orbited.js; adding ORBITED_PORT = 8000; and not using the proxy any more.

This backwards compatibility is a convenience only — Orbited has changed focus away from the 0.3.x architecture. Instead of acting as a simplistic message queue that can route messages to the browser, Orbited is instead a socket proxy. It exposes a TCPConnection to javascript, and relays packets via actual, raw TCP connections on the back end. This way you can use Orbited as a means to talk directly between an out-of-the-box IRC server and the browser. Indeed, in the /static/demos/chat directory you can see an example of connecting directly to an IRC server from javascript.

This new architecture is much better at integrating a browser with arbitrary network servers. Another benefit is that we can connect the browser directly to a message queue, such as ActiveMQ, and use the topic (publish/subscribe) and queue semantics provided out of the box. Furthermore, ActiveMQ comes with a configurable replication strategy out of the box. Take a look at /static/demos/stomp for an example of publish/subscribe with Orbited+ActiveMQ.

The Orbited Blog is Back Online

June 22nd, 2008

We had a number of technical issues for the past few months. Adrian Weisberg has recently taken over as the administrator and editor of this blog. Now that we’re back on track you can expect blog activity to resume.

Upcoming Orbited 0.4.0 information

February 23rd, 2008

The orbited 0.4.0 branch has been under heavy development for the past month and we are finally starting to near an alpha release. While this release won’t be immediately useful to Comet developers, it can give you a good idea of what will be in the final Orbited 0.4.0 release.

New Website

Das-q and maddiin (Christian and Martin czura) from the #orbited IRC channel on freenode have been hard at work on a new website for Orbited written in DJango. The website features blog integration, a bug tracker, integrated documentation, and an IRC announcement bot which publishes to the channel whenever any website content changes. We hope to use this website as a rallying point towards explaining every facet of the 0.4.0 branch and documenting its usage for all cases.

ORBIT Protocol (OP)

The 0.1-0.3 releases all use the current description of the OP protocol which is very minimalistic. In the 0.4 branch we’ve re-written the protocol. We have an in-progress attempt to document it in the svn repository: OP Documentation. This protocol includes a means for calling back to the application for events such as signon, signoff, delivery success, and delivery failure. The callbacks don’t have to occur over OP; there is an option to have callbacks issued as http requests to the application. That means you can simply implement a url like /orbited/signon in your application, and specify it as the signon callback. Whenever a user connects to orbited, your application will be notified. I think the most useful callback is likely signoff, because its very hard to create independently on the client-side. Additionally, the signoff callback will delivery a list of all messages that failed to be delivered in the case of a browser timeout. One obvious use case would be a dead-simple way to persist messages that failed to be delivered but should be delivered the next time the user signs on.

STOMP Protocol

Marcus Cavanaugh brought the STOMP protocol to our attention as a possible means of replacing OP. The reasoning is that STOMP has client implementations in many languages, whereas OP only currently has implementations in Python, Ruby, PHP, and C#. The new OP protocol has no client-side implementations. The Orbited 0.4 branch will therefore include a STOMP server as a means to interact with the server int he same way an app could over OP. The reason we aren’t just completely getting rid of OP is that its easier for us to think in terms of OP for Orbited, and then map functionality on to STOMP. OP is intended as a perfect fit for the domain, by definition, so its easier to experiment with. We don’t really expect to see OP client implementations in other languages, and as such we won’t worry to much about breaking backwards compatibility for OP.

Layered Communication

I recently participated in an article series discussing Bayeux. I argued that Bayeux lacks many of the facilities you’d expect from a communication protocol in its domain. The core developers are all in agreement that Bayeux is not a good standard for many reasons, but primarily flexibility, scalability, and reliability. Therefore we are implementing three tiers of communication in Orbited. The first is the transport layer, and it is identical to the interaction of Orbited 0.3.x. The Orbited.js file is an implementation of the transport layer of Orbited. It allows for unreliable server->client delivery of messages.

CSP

The Session layer in Orbited 0.4 is CSP. Some information about the specification can be found here, though is highly subject to change even before the alpha release. This layer is used by including /_/CSP.js in the application html document, and then using the CSP object much in the same way that the Orbited object from Orbited.js was previously used. The main difference is that the CSP layer guarantees message delivery except in the face of a browser-side timeout, even over frequent reconnects due to a spotty network. Also, CSP handles pinging the browser to handle connection state. CSP and the new hooks in OP together provide are a powerful unidirectional messanging platform for modern application frameworks like Django, Pylons, Rails, or PHP. The application has only to deal with dispatching messages to the client, and it gets reliability and connection state management for free.

Revolved

Revolved is a bi-directional, publish subscribe, and peer-messaging layer similar in many regards to Bayeux. In the past we’ve stuck with the minimalist approach of only implementing unidirectional messaging for the purposes of scalability. You can find an explanation of why in my conference slides from AjaxExperience. But the primary request by users is for publish subscribe functionality. We want to be able to offer this functionality, but not at the cost of scalability. We experimented with Revolved in the 0.3.x series by creating a Revolved plugin and a corresponding javascript file. This plugin is almost wholly undocumented besides this mailing list exchange, the plugin was basically a success. Instead of including Revolved as a plugin though, we are integrating it with the core of Orbited 0.4. It is built on top of CSP so as to inherit all of the reliability guarantees. The back-end hasn’t been fully fleshed out though. The 0.3.x series included a simple replication mechanism for running multiple Revolved nodes. We don’t want to include this type of functionality in Orbited 0.4.0 directly because the problem of scaling publish/subscribe nodes in a general way is a very tough problem. Therefore, Revolved will include a plugin api itself for implementing various backends for publish/subscribe, such as IRC. Our ultimate goal is to provide a general method of distributing Revolved so as to provide the best possible throughput and latency for publish/subscribe while also keeping good peer messaging performance. We think Revolved in 0.4.0 will be a better bet than Bayeux, but it is still a long shot from being generally scalable to hundreds of thousands of users. I imagine it would work well for tens of thousands of concurrent users though.

Deployability

As mario mentioned, your pyevent worries are over. We have implemented a pure-python library that emulates pyevent’s network api, providing select, poll, and epoll backends. It will also check for pyevent at runtime and use that if it is installed. This means that installing orbited 0.4.0 is a matter of running “easy_install orbited” and thats it.

Addtionally, Orbited is now a fully compatible WSGI server. The implications are that it can act as the deployment server for many python frameworks, such as Django, Turbogears, and Pylons. This won’t help PHP or Rails developers, but it is a step in that general direction. I will note that this type of deployment binds performance of the Comet layer to your application layer, but isn’t necessarily an obstacle towards horizontal scalability.

Conclusion

Orbited 0.4.0 represents many changes from previous versions. We have three particular goals: 1) Deployability, 2) Integration with synchronous frameworks, 3) Better Server <–> Browser communication protocols. The alpha release will provide a good preview of these features. If you have any suggestions or comments, don’t hesitate. Now is the time to voice them. I don’t expect we’ll see a set of changes nearly this radical again. The 0.4.0 series will likely be similar to an eventual 1.0 release.

Your pyevent Worries Are Over

February 17th, 2008

One of the most frequent problems people have with Orbited generally surfaces before they write a single line of application logic — before, in fact, they successfully start the Orbited server for the first time. This problem is pyevent. It’s a brilliant piece of software, but many of us want to build small projects or simply prototype without having to install two sets of additional headers and gcc.

To most of you, I’m sure this doesn’t sound like a great hardship. Comet developers, however, are not most of anything. We’re strange people and we often work under strange conditions. My house loses its internet connection every few minutes; other developers are subject to the irrational decrees of their admins, or the arcane limitations of their indie operating systems. People have literally decided not to use Orbited because they couldn’t compile pyevent properly. We’re modern folks, and the rule is: make it work for everyone.

So Michael Carter and I were talking about the upcoming release of shiny new 0.4, and we decided that this had gone far enough. So we wrote up a little module to import as event anywhere pyevent would normally fit, and it did its job splendidly. That is, if it detected pyevent, it would use it; otherwise, instead of blowing everything up, it used epoll, or poll, or select.

The initial version supported the most widely-used pyevent commands: ‘read’, ‘write’, ‘timeout’, ‘dispatch’, ‘loop’, and ‘abort’, as well as an optional ‘initialize’ for specifying the preferred order of methods to try. This is useful because the best method can vary from job to job. Select, for instance, is pure python and performs as well as epoll for limited levels of concurrency, but will completely explode if the hard-coded connection limit (FD_SETSIZE) of 1024 is exceeded.

This interface was enough for a pyevent-like event notification system to do its job, but at this point rel (Registered Event Listener) was far from a perfect, drop-in substitute for pyevent, whose extended api has several features that I’d never really used.

event.sys is just sys but nevertheless could conceivably be called by some well-meaning coder.

event.init builds/clears the event queue. It also cancels system signal callbacks, but does not reset the default callbacks. This is not clean behavior, and rel corrects it.

event.signal is pyevent’s “simplified event interface.” It is sort of a combination of event.timeout and python’s built-in signal module.

event.event is a verbose, general interface for read, write, signal and timeout events. Various apps, including Orbited 0.3, use it.

So I rewrote rel and tested it against pyevent’s test.py, and at first it seemed to do its job, performing identically to pyevent. Then I took a closer look at pyevent’s test code, and discovered that one of the test methods was disabled (there was no call to event.dispatch, so the code was never executed). I added that line, and immediately got an error — still, mind you, using pyevent. Here’s what the code looked like:

def __signal2_cb(sig):
    ...
...
event.timeout(2, __signal2_cb)

For those of you unfamiliar with pyevent, event.timeout typically looks like this: “event.timeout(seconds to wait, callback function, arguments to pass to the callback function)”. In the code above, there were no arguments specified, so it blew up when it tried to call __signal2_cb with no arguments. I changed “def __signal2_cb(sig)” to “def __signal2_cb(sig=None)” to get past that, which made everything run, with one catch: with this test method enabled, pyevent ran about 5 seconds slower than the other methods. Very strange.

So I set about isolating the anomaly, and put together test2.py (in this egg), which demonstrates the odd behavior. Surely, a kindly reader will have some idea what’s going on. Anyway, I moved on.

The finishing touch (for now): Michael wrote a magical python path override function that essentially replaces sys.modules['event'] with rel. This means that to rel-ize any pyevent app, all you need to do is add

import rel
rel.override()

to the top of your start script. Magic! This will use the default method ordering (pyevent, epoll, poll, select) unless you specify an order of your own with rel.initialize([list of methods]). Either way, rel will emulate the pyevent api with the first method in the list that works.

(Note: To use pyevent, get pyevent + python development headers, libevent headers, gcc. For epoll, install python-epoll. Without these, rel will fall back to poll, which comes standard on most Unix systems, or as a last resort, select, which works pretty much anywhere.)

Anyway, Michael’s magic override script enabled me to test rel with three pyevent-driven servers - Orbited 0.3.1, FAPWS 0.7, and apricot 0.2 - in ipython without changing a line of code. It works without a hitch for these, as well as for the pyevent-based HTTP/1.1 client Ludo.

I’m very happy about this. It means that we can all keep developing for pyevent, and with the mere inclusion of two lines of safety net, deploy our software anywhere. Anyway, give it a try, see what you think, let me know if you can solve that test2.py mystery. And let me know if you think of any other methods that rel should support. (Epoll, poll, and select are currently implemented; Kqueue is on the way).

Also, Michael told me that he discussed the future of pyevent with the current maintainer, Dug Song, and some other interested parties. There is a good chance that libevent’s evhttp and possibly evdns will be exposed in the pyevent 0.4 release. Rest assured that rel will support this functionality as well, and without any external dependencies.

google code: http://code.google.com/p/registeredeventlistener/ ;)

Announcing Orbited 0.3.0

December 27th, 2007

The Orbited team is happy to announce the release of Orbited 0.3.0.

Orbited 0.3 is an evolutionary update to Orbited 0.2, with several new and exciting features, improved stability and efficiency, and some groundwork for future improvements.

Most notably, the Revolved publish/subscribe system has been rolled into Orbited. To start it, visit `http://orbited_server:8000/_/revolved/manage/`.

Other improvements include:

  • Orbited session system
  • Event buffering
  • Event retries
  • New global config options for retry limit and session timeout:
  • Retooled error handling
  • Removed some confusing and unnecessary errors
  • Added HTML error messages for bad requests from browsers
  • Reworded error messages for clarity
  • Plugin management system and Admin plugin
  • Can start or stop any installed plugin at runtime
  • To start the admin plugin, visit http://server:8000/_/admin/manage/
  • C# Orbited client API

Coming soon:

  • More documentation
  • Polling and long-polling transports
  • Benchmarks!
  • More tutorials

Thanks to:

  • Eugene Ramirez for work on the C# client
  • Frank Salim for help with the plugin system and admin plugin
  • Everyone who provided feedback on the Orbited mailing list and IRC channel.

Announcing Orbited 0.2.0

October 26th, 2007

The Orbited core development team is pleased to announce the release of Orbited 0.2.0.

Core Team:

  • Michael Carter
  • Jacob Rus
  • Mario Balibrera

Our Goals:

  • Support all major browsers without loading bars, clicks, or hour glasses
  • Create a standard, interoperable Orbited js library
  • Refactor transport architecture to achieve greater extensibility
  • Create a plugin system
  • Standardize logging / debugging output
  • Standardize the urls
  • Increase the separation of orbited sub modules

Orbited 0.2 is nearly a complete rewrite of most of Orbited’s internal components. It reorganizes architecture while leaving external interfaces nearly unchanged.

There are many great new features that will be documented over the course of the next few weeks via blog posts and updates to the core documentation. In particular, stay posted for information about the new plugin system. We provide a simple admin application as an example of how to create a plugin. Also stay posted for documentation on creating new transports by extending orbited’s Comet transport system.

We would like to thank the following contributors of documentation, patches, tutorials, and ideas for their time (in no particular order, and apologies to anyone we forgot to list):

  • Paul Johnston
  • Matthew Desmarais
  • Mike Zaic
  • Rob Morris
  • Alex Russel
  • Max Nickel
  • Christian Czura
  • Ciarán Walsh

Status of Orbited and AjaxExperience

October 4th, 2007

I just received word that my talk titled “Comet for highly scalable applications” has been accepted to AjaxExperience. From the look of it, AjaxExperience will be a much nicer conference than AjaxWorld, at least in how they treat speakers. I’ve had personal assistance with scheduling from the conference organizer and they are paying my airfare. I’ll be giving a similar talk to that which I gave at AjaxWorld. As I’ve mentioned before, you can find those slides here, and the speaking outline here.

Orbited is coming along nicely. I’ve gone over all the patches that were submitted in the last month. I accepted a few directly, but most I had to rewrite. The reason is that we are skipping Orbited 0.1.6 and moving directly to Orbited 0.2.0 as most of the codebase has been changed. There’s been a complete restructuring so it’ll be easier for developers to get involved in specific parts of the project without understanding everything. In particular, creating and understanding transports will be very straightforward and will not require changing the Orbited source. We also moved away from the pipe-based urls like “/location|user,session,transport” in favor of standard query strings: “/location?user=x&session=y&transport=z”. I personally liked the look of the old urls, but the end-user never sees them, so the result was that Orbited sometimes breaks ie with no tangible benefit.

Stay tuned over the next week for Orbited 0.2.0. We’ll need help porting the documentation, though only minor changes will be needed in the javascript and application code.