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.
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.
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.
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
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.
#!/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()
# 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
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:"))