About

Cyclone is a low-level network toolkit, which provides support for HTTP 1.1 in an API very similar to the one implemented by the Tornado web server - which was developed by FriendFeed and later released as open source / free software by Facebook.

Key differences between Cyclone and Tornado

  • Cyclone is based on Twisted, hence it may be used as a webservice protocol for interconnection with any other protocol implemented in Twisted.
  • Localization is based upon the standard Gettext instead of the CSV implementation in the original Tornado. Moreover, it supports pluralization exactly like Tornado does.
  • It ships with an asynchronous HTTP client based on TwistedWeb, however, it's fully compatible with one provided by Tornado - which is based on PyCurl. (The HTTP server code is NOT based on TwistedWeb, for several reasons)
  • Native support for XMLRPC and JsonRPC. (see the rpc demo)
  • WebSocket protocol class is just like any other Twisted Protocol (i.e.: LineReceiver; see the websocket demo)
  • Support for sending e-mail based on Twisted Mail, with authentication and TLS, plus an easy way to create plain text or HTML messages, and attachments. (see the e-mail demo)
  • Built-in support for Redis, based on txredisapi. We usually need an in-memory caching server like memcache for web applications. However, we prefer redis over memcache because it supports more operations like pubsub, various data types like sets, hashes (python dict), and persistent storage. See the redis demo for details.
  • Support for HTTP Authentication. See the authentication demo for details

Advantages of being a Twisted Protocol

  • Easy deployment of applications, using twistd.
  • RDBM support via: twisted.enterprise.adbapi.
  • NoSQL support for MongoDB (TxMongo) and Redis (TxRedisAPI).
  • May combine many more functionality within the webserver: sending emails, communicating with message brokers, etc...
  • Integrates on existing twisted based projects easily
  • Leverages all Twisted protocols

Benchmarks

Some of the most popular Python web frameworks have been tested with a simple "hello world" application in order to compare performance against Cyclone.

All the tests were performed using ApacheBench (ab -n 100000 -c 25 http://host). All servers were single-threaded process, with no proxies in between. Django was also tested, using both mod_python and WSGI - but the results aren't published because perhaps we missed extra configurations or tuning which led to bad performance.





We've uploaded a chart previously which would lead to a wrong conclusion of tornado/cyclone req/s rate over twisted and gevent. It was a honest mistake due to the haste that we've put the site online. A more balanced benchmark can be found here. Requests per second are always presented as a form of measure efficiency or performance of a given framework, but we want to go far as offering more sustained and predictable streaming rates along with bundled resources and the maturity of the Twisted framework.

Twisted 10 and 11 are way faster than 0.x versions, and cyclone leverage the improvement in performance as well. Back in the days, cyclone used to be about 30% slower than Tornado, but now it's faster.

Architecture

The general cyclone architecture is as follows



Over the web component you will find Tornado's templating engine, a bottle-like DSL and an authentication engine. Cyclone also provides an email handler API to integrate email generation and delivery, Redis and MongoDB drivers that are reference for twisted, WebSocket and Server Sent Events protocols.

Chances are that the changes needed to migrate from Tornado to Cyclone are minimal if you are not using ioloop specific code. By being a twisted protocol you can start a tornado app and a conch (ssh) server in the same context.

Who's using it ?


Nuswit

RestMQ - Message Queue

MotorSW - Auto shop and car dealer management SaaS

BrickLayer - Building/Packaging/Repository uploading framework

freegeoip.net - a web service for IP geolocation

musta.sh - recognize people's face and put mustaches on it

Brazilian Ministry of Education on Digital Library project

Code samples

You can find example and demo for each one of cyclone's features at the GitHub repo. Below is a sample Hello World application both using straight execution and the TAC application that comes with twisted.




Tornado Application Style

    #!/usr/bin/env python
    # coding: utf-8

    import sys
    import cyclone.web
    from twisted.python import log
    from twisted.internet import reactor

    class IndexHandler(cyclone.web.RequestHandler):
        def get(self):
            self.write("hello world")

    class Application(cyclone.web.Application):
        def __init__(self):
            handlers = [
                (r"/", IndexHandler),
            ]

            settings = {
                "static_path": "./static",
                "template_path": "./template",
            }

            cyclone.web.Application.__init__(self,
				handlers, **settings)

    if __name__ == "__main__":
        log.startLogging(sys.stdout)
        reactor.listenTCP(8888, Application())
        reactor.run()
	

Twisted Application Style

    # coding: utf-8
    # twisted application: foobar.tac

    import cyclone.web
    from twisted.application import service, internet

    class IndexHandler(cyclone.web.RequestHandler):
        def get(self):
            self.write("hello world")

    foobar = cyclone.web.Application([(r"/", IndexHandler)])

    application = service.Application("foobar")
    internet.TCPServer(8888, foobar,
        interface="127.0.0.1").setServiceParent(application)
	
    for testing:
    /usr/bin/twistd --nodaemon --python=foobar.tac

    for production:
    /usr/bin/twistd --pidfile=/var/run/foobar.pid \
                    --logfile=/var/log/foobar.log \
                    --uid=nobody --gid=nobody \
                    --reactor=epoll \
                    --python=foobar.tac
	



Bottle DSL and sqlite

import sys
import cyclone.sqlite
from cyclone.bottle import run, route

@route("/")
def index(web):
    web.write("try /sqlite\r\n")

@route("/sqlite")
def sqlite_get(web):
    v = web.settings.sqlite.runQuery("select strftime('%Y-%m-%d')")
    web.write("today is " + repr(v) + "\r\n")

run(host="127.0.0.1", port=8080,
    log=sys.stdout, # or any file descriptor
    static_path="static", template_path="template",
    sqlite=cyclone.sqlite.InlineSQLite(":memory:"))