Drinkin gasoline and wine

post zenmachine musings

Cyclone - a Twisted Based Tornado Implementation

Overview

Some time ago, a company called FriendFeed released Tornado, a neat web application server for python. After some press and unquestionable results it was discussed whether it should have used the Twisted Framework foundation instead of implementing a new ioloop. Long history short, Tornado shares some similarities with Twisted, but the programming API is better looking than twisted.web.

After some trials by different people Alex Fiori forked Tornado and bundled it with a Twisted backend and some other goodies - calling it cyclone. After a while I started using it to build RestMQ and started contributing code for WebSockets and other drivers. There is a lot of sense in combining both worlds as Twisted has an extensive ibrary of protocols and clients, a well defined programming model (like it or not, based on deferreds/futures and generators) and mature cross-platform ioloop implementation. Cyclone’s gettext implementation was merged back some time in the past and we constantly merge from upstream on interesting features.

I do most of my coding in python splitting time between cyclone and gevent and right now I gotta say that cyclone has great features that compete in terms of productivity with Tornado.

Code that is build on tornado will run easily after correcting the package names. On the parts related to ioloop, there are the same mapped functionality on twisted - such as timers and pools. To build new protocols you can leverage LineProtocol and other interesting tx classes. The best part is taking advantage of drivers. In an evented loop, if you use a regular driver that can block (pause while waiting for an answer from network or heavy calculation) the other operations are also halted.

If you have a defined programming model to deal with it (which both tornado and twisted defined), it is a matter of yielding at the right moment or using a deferred return to realize the result of the operation later. That can lead to a kind of callback hell both for reading code and profiling it but there are few abstractions that will go far away from it.

Interesting Cyclone features

Tornado core (ioloop) was changed by a twisted based factory which yield the right reactor. Over this structure the protocol implementation and clients were adapted to use it with minimal to none interface changes.

The most affected module initially was cyclone.web but the whole structure changed and got bundled drivers as mongodb, redis, sqlite and protocols as XMLRPC, JSONRPC, websockets and sse. There is an email module already which can serve as template-to-message app, based on TwistedMail. All these features are natively asynchronous.

Beyond that, a cyclone app is a twisted protocol and can take advantage of the surrounding structure as plugins and PyDirector/cpu affinity. It was easy to merge or create due to the synergy based on already existing twisted applications. There is also an application skeleton and a minimal bottle.py DSL port - both allowing for quickstart web applications.

Much of the authentication and authorization is done over decorators, allowing for clean code - along with the inline deferreds:

class IndexHandler(cyclone.web.RequestHandler):
    @cyclone.web.authenticated  # triggers authentication
    @defer.inlineCallbacks      # allows for inline callbacks
    def get(self):
        result = yield self.do_download()  # inline callback, no need to explicitly added 
        self.write(result)

That alone may help on the callback spaghetti but it keeps being twisted.

Evented I/O intermission - is it faster ?

No. It’s just a choice of multiplexer to allow for better time utilization. It helps scaling better the same resources as other approaches such as greenlets over an evented loop (gevent). The most important thing to note is that they are not a substitute for threads of VM limitations. My setups usually are 1 or 2 instances per core, with affinity and a load balance in front of them. This can be done in a different manner as using threadpools or even processes.

Last year I presented at Sao Paulo Perl Workshop and OSCon on this subject and the feedback that I got is that most of the time the very application gets complex so the matter of evented I/O ends up being one of the things that shapes these changes (for good or bad) but not the safeguard or guarantee that the quality will keep up.

EOF

Check the code and try it for yourself. I consider that using cyclone is a good and gentle intro to twisted as it sets a clear objective over web applications. There are plenty of good code at the demos directory and the app skeleton already comes up with a bootstrap.css based application boilerplate. Also, send patches.

Comments