create blog

go home go home
  1. about
  2. code
  3. wiki
  4. blog

Archive for the ‘Comet’ Category

Roots Deliver Nutrients

Monday, December 7th, 2009

Push is complex. I tried to figure it out with ActiveMQ, Orbited, and friends awhile back, but it just seemed too complicated. It made me feel like I was not very smart—obviously, some people understand this thing, but I don’t. I am a bit too prideful, and do not like feeling unintelligent.

But I need messaging.

I don’t care about queuing or persistence. For web applications (what I work on) messages should be delivered as quickly as possible. Any “queues” can be in-memory, and any message should not remain in a queue for more than a minute—if the client was disconnected for any duration, it would likely be better off re-downloading that receiving incremental updates.

So, whatever these systems do, they seem too complicated (I could be wrong—as I said, I have trouble understanding them).

My requirements:

  • Normal Authentication. Clients should go through the “normal” web app to subscribe to events.
  • Easy. It should be very simple and easy to understand.
  • Quick. I should be able to add Comet to an app in ten to fifteen minutes, maximum.

I found out that it is not only possible to write messaging servers on one’s own—it is relatively easy. So I wrote one.

I’ll stop the backstory here, though there is plenty more—tell me if you want any of the boring details about Dolores and the rest.

Roots

Roots—codenamed Dobby after the Harry Potter character—is a messaging server that has a built-in long-polling server (among several other things).

Roots is made up of many small message-passing pieces. They plug together, just like Lego pieces. Each piece is simple, and the connection between the pieces are even simpler: a single function, called “update.”

If you are interested, I’ll go over that more thoroughly in “Implementation.”

How Roots is Used

Above All, Roots is Easy.

It takes me about fifteen minutes to use Roots to add push to a SproutCore application (starting with a fresh clone of Roots—which, by the way, does not need any configuration for testing). It should only take me five minutes, but I am often a bit slow of a coder.

There is a brief semi-tutorial on GitHub. The following is based on that tutorial, but has some extra background information.

So, a bird’s-eye view: unless you implement the server-side in Roots itself, you will have three components in any application: back-end (Rails, Django, etc.), front-end (SproutCore), and Roots.

  • Client—such as Pomona, the SproutCore framework for Roots) connects to a Roots server.
  • Roots sends the client (Pomona) an id.
  • The client calls predefined URL to connect to events.
    • The URLs are part of the back-end (Django, etc.)
    • The ID is sent with these requests.
    • Clients (Pomona) usually will keep track of what server-side events you are listening to and can reconnect automatically if the Dobby connection is interrupted—you just provide the client with the URLs and the events to listen to.
  • Back-end tells Roots to connect client to events—and can perform any authorization it wishes.
  • Back-end sends updates to Roots.
  • Client receives notifications—everyone’s Happy!!!

What You Do (for SproutCore)

You don’t have to do much:

  1. Add Pomona to your SproutCore project.
  2. Add a Roots connector to your server-side project:
    • Cornelius for Python
    • Others as I add them (I plan Ruby and PHP)
    • Implement your own (the Python version of the better protocol—Dudley—is 45 lines counting whitespace and comments).
  3. “Update” Roots (call update(path, message) from your back-end—Django has handy model post-save signals you can use).
  4. Add some URLs to your back-end that connect clients.
    • URLs accept client ID in them (/my/connect/AN_ID_HERE or /my/connect.src?id=AN_ID_HERE)
    • URLs should accept POST of a simple JSON array of path/event names.
    • For each path/event/whatever you want to call it, call connect(id, path) on your Roots connector.
    • Initialize Pomona and connect to Roots in your SproutCore Data Source.
    • Accept incoming data!

A more detailed tutorial is here on GitHub with the rest of Dobby (Roots).

Open Question: Would anyone like a tutorial on setting up a Django back-end for the SproutCore Todos tutorial, with an extra step of adding Comet at the end? Done!

Implementation

Roots/Dobby is currently written in Python. I am working on a rewrite in node.js, because node.js is super cool—and, secondarily, it means that some of the same code used in the server (for instance, the message dispatch code) could be used on the client.

Roots has many small parts that hook together in a very simple way. At Roots’s core is Thestral, a simple interface that requires implementors to do two things: have an id and implement a single method: update(sender, path, message)—where sender is an originator, path is an event name, and message is some data to send.

Here are some of the components:

  • Dolores: This is the main controller. It assigns IDs to all Thestral instances. It itself is a Thestral instance, and sends its updates to “delegates”.
  • Pig: An “Owl,” or, really, a dispatcher; it knows who receives what message. It is often one of Dolores’s delegates. Specially-formed messages, tell it to connect certain Thestral ids with certain events—for instance, the event “::connect” with message “12345->my/data” would connect Thestral “12345″ to “my/data”.
  • Imperio. A somewhat pathetic text-based protocol, with accompanying receiver server (a bit like Dudley, which should be used instead). In addition to reading from a server, Imperio supports writing to anything with a write() method, and as such, is used for logging as a delegate of Dolores.
  • Dudley. Dudley is not a delegate of Dolores. Rather, Dolores can be its “delegate” (or, in this case, “receiver”). Dudley is an HTTP server, and receives messages over HTTP and funnels them to a receivers (Thestral implementation), and Dolores is its default receiver.
  • Firenze. Firenze, like Dudley, is an HTTP server; but instead of being a receiving server, it is a long-polling push server. It is not usually Dolores’s delegate—you don’t want every client to get every message. Instead, Firenze merely registers itself with Dolores to get an ID, and lets others register it (for instance, a client could send Dudley a message, which, through Dolores, would be received by Pig to make the connection).

Above all, it is simple. The longest of these pieces is Firenze, at 280 lines of code (50 of which are intro comments I used to explain it to myself and spec it while planning).

That’s All!

I hope you find this helpful. I am excited about porting Roots to node.js; progress is somewhat slow, as I am quite busy (I am a student, and finals for all four classes tomorrow!)

What I am most looking forward to is how Pig (the dispatcher) could use the same code in both the client (Pomona) and the server—the routing is quite similar; in one case, you want to route to handlers/callbacks, in the other, to update functions on Thestral instances—pretty similar; even identical.

I am also looking forward to experimenting with two-way push, where all data is transferred through push—completely eradicating the expectation of a response to a request, and providing only a single receive/send code path on both client and server.

Anyway, I think this is all super cool. :)

Thanks to Endash for the nice title. :) Roots (called Dobby in development) is available on GitHub.

P.S. I like the name Dobby, but Roots is better P.R.

Running Orbited (or other Twisted Services) as a Windows Service

Tuesday, August 4th, 2009

It took me awhile to figure out how to run Orbited and Dolores as Windows Services. Unfortunately, while I love Mac, almost every other computer at my workplace is Windows, including the servers. When deploying my web application, I can’t really just leave instructions like: “When you restart the server, please run these two scripts.”

They need to be Windows Services.

Thankfully, it isn’t actually too hard. You just need a couple of Python packages (which I actually don’t remember installing, so they may have come with Twisted or Python), and then all you have to do is write a couple of very simple scripts. Here are mine:

Orbited Service

import OrbitedManager # a copy of orbited.start with some very minor modifications, which I'll include after the break
import time
 
# Service Utilities
import win32serviceutil
import win32service
import win32event
 
class WindowsService(win32serviceutil.ServiceFramework):
	_svc_name_ = "Orbited"
	_svc_display_name_ = "Orbited Server"
 
	def __init__(self, args):
		win32serviceutil.ServiceFramework.__init__(self, args)
		self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
 
	def SvcStop(self):
		self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
		win32event.SetEvent(self.hWaitStop)
 
	def SvcDoRun(self):
		import servicemanager
		OrbitedManager.start()
		while True:
			retval = win32event.WaitForSingleObject(self.hWaitStop, 10)
			if not retval == win32event.WAIT_TIMEOUT:
				OrbitedManager.stop()
				break
				time.sleep(5.0)
 
 
if __name__=='__main__':
	win32serviceutil.HandleCommandLine(WindowsService)

Dolores Service

import Dolores
import time
 
# Service Utilities
import win32serviceutil
import win32service
import win32event
 
class WindowsService(win32serviceutil.ServiceFramework):
	_svc_name_ = "Dolores"
	_svc_display_name_ = "Dolores Server"
 
	def __init__(self, args):
		win32serviceutil.ServiceFramework.__init__(self, args)
		self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
 
	def SvcStop(self):
		self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
		win32event.SetEvent(self.hWaitStop)
 
	def SvcDoRun(self):
		import servicemanager
		Dolores.start()
		while True:
			retval = win32event.WaitForSingleObject(self.hWaitStop, 10)
			if not retval == win32event.WAIT_TIMEOUT:
				Dolores.stop()
				break
				time.sleep(5.0)
 
 
if __name__=='__main__':
	win32serviceutil.HandleCommandLine(WindowsService)

That’s the basics, but as you may have noticed, I also changed Orbited’s start script. I did this because the original did not play nicely with threads.

Instead of changing it in its original location in the filesystem, I created a copy of it in the local directory (as you can see, I’m not importing Orbited.OrbitedManager).

(more…)

Dolores & Cornelius, Sitting in a Tree. Of Comet, Orbited and Python.

Tuesday, August 4th, 2009

I’ll give you a moment to allow you to get that disturbing image out of your head.

I have been working on a web application. I’ll hopefully be able to post the application soon.

I needed the server to be able to send updates to the client via Comet. I’d never done any Comet before, so I wasn’t sure what to do.

A lot of articles had examples making use of message queues and who knows what else. They were a tad overwhelming. While a message queue of some sort may eventually be a good idea, for now, all I needed to do was push a non-vital progress indicator for a job (15%, 20%, 30%, and so on) to the client so the user could see what was happening. I wanted more than one job to be supported, and the overall system should eventually allow sending updates when elements in the user’s filesystem are updated.

So, I’d like to be able to send:

UPDATE process/at/my/process/path; {progress:.5}
UPDATE file/at/my/file/path.txt

In short, I’d like to send paths, along (sometimes) with little tiny messages.

Also, each instance of the web app should only receive updates to paths they are listening to, and they should only be allowed to listen to paths they actually have access to. Or, at least, that should be the case once I implement permission management in the app &emdash; right now, anything goes, which is fine for the moment because it is limited to test use on our local network.

So, I made a little script, called Dolores, which handles all of this, and another script, named Cornelius, which connects to it (incidentally, there are two Corneliuses: a Python one, and a JavaScript one).

I have posted it here in case anyone is interested. I used Orbited in JavaScript to connect to the server. In a separate post in just a few moments, I’ll write about something a tad more useful than the Dolores & Cornelius code I’m writing about here: how to run the Orbited (and, actually, Dolores) as a Windows service.

(more…)