Ok this is first of series post about python Tornado framework and intent to teach people Tornado and some of the best practices. Over the series of post I will assume that you have beginner knowladge about python if not check google there are tons of documentation and lately online courses about every language.
What the hack is Tornado ?
Tornado is simple, powerful, easily scalable python web server and web framework. Most of the people either know Tornado as webserver or as web framework but its actually both. It initially created by Friendfeed guys and after the acquisition by Facebook, it now open source and has great vibrant community around it and actively in development. If you wanna play it for a while must have resources Tornado offical web site & source code, Tornado google group and of course google & stackoverflow.
There are lots of alternative what makes Tornado different is that it tries to solve C10K (concurrent ten thousand connections) problem. If you dont wanna read that long but great article from Dan Kegel here is the point of the author “Its time for webservers to handle 10.000 connections at one instance we have enough hardware and infrastructure for that why the hell we could not do it ? And then goes into details of traditional web server approach like thread-per-connection, process-per-connection. The rest of the article is about new approaches that webservers can take in order to achieve that C10K (concurrent ten thousand connections). Some of the approaches get help from OS kernel to achieve that high number concurrent connection which also used by Tornado select and epoll (unix systems) and kqueue in some FreeBSD systems (by the way this is the reason that Tornado is not a good candidate to work on Microsoft stack altough people seems to be using it with minimum problem). Meaning that in order to push boundries of Tornado you have to run it under some kind of unix variant.

Now if we look at the approach that Tornado takes and problem try to solve we can conclude that it is high performance web server+framework. Now we know that its a high performant web server what about web framework side ?. If you look at the framework its very small but elegant, builtin security mechanishms like secure cookies, cross-site-forgery, social network authentication like facebook, twitter, google, simple pythonic templating engine, built-in mysql wrapper for most of the operations and asynch approach, how about some websocket support for your application ? its already built-in. And greatest part of it if you dont like some built-in feature just switch with another one like templating engine too basic for you just take jinja and use.
Tornado is greate for applications that need high concurrency and performance but of course its not one framework for all kind of applications. If you gonna build new CMS or need very detailed web framework Tornado may not be for you. You have idea about some kind of interactive realtime web app such as real time analytics engine, social app, even you can use Tornado as TCP server, it does not restrict you.
Install Tornado
Enough background info lets install Tornado. I am gonna show you unix installation so if you on windows you should check out tornado offical site, last time I check it was easy to install on windows.
$ curl -L -O http://github.com/downloads/facebook/tornado/tornado-2.4.tar.gz
$ tar xvzf tornado-2.1.1.tar.gz
$ cd tornado-2.1.1
$ python setup.py build
$ sudo python setup.py install
Also if you dont wanna deal with multiple command like above you can use pypi or easy_install to install lateset version of Tornado which is 2.4 as I am writing this article.
How about Hello World App ?
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
application = tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Here is hello world application. Lets dive into it. If you are writing Tornado app some of the things you must be familiar.
You have to import at least tornado.ioloop & tornado.web modules. There are minimum requirements for you.
In order to handle Http request tornado have RequestHandlers which again reside in RequestHandler package. Request handler have all of the Http methods like GET, POST, HEAD etc. but you just implement what you need. In hello world application we just implemented GET method if you make POST request you probably get error stating that POST method does not implemented.
Another thing is application object. We have created instance of Application object and give mappings of handlers, our MainHandler will be answering requests from “/” path. If you have further handlers you can just keep adding to there and good thing is you can easily use regex in order to match your RequestHandler. We set application to listen 8888 if this is the port you wanna run the app.
Last thing and most important is getting instance of IOLoop which we only have one. Yeah right all that high throughput comes from one thread so you have to be careful while using. I can only say that right now that its the result of async and non blocking io. (For further information you can check C10K article). Let me create another version of application with more than one handler.
import tornado.ioloop
import tornado.web
import tornado.options
import tornado.httpserver
import os,logging
from tornado.options import options,define
log = logging.getLogger(__name__)
"""
create definitions here like facebook, twitter keys
port number etc
"""
define("port", default=8888, help="default port number", type=int)
define("facebook_api_key", default="", help="facebook api key for facebook graph api")
define("facebook_secret", default="", help="facebook api secret")
define("twitter_consumer_key", default="", help="twitter api key")
define("twitter_consumer_secret", default="", help="twitter secret key")
class Application(tornado.web.Application):
def __init__(self):
handlers = [
(r"/", MainHandler),
(r"/another", AnotherHandler)
]
settings = dict(
app_title = "Hack Skeleton",
template_path = os.path.join(os.path.dirname(__file__), "templates"),
static_path = os.path.join(os.path.dirname(__file__),"static"),
cookie_secret = "",
login_url = "/login",
facebook_api_key = options.facebook_api_key,
facebook_secret = options.facebook_secret,
twitter_consumer_key = options.twitter_consumer_key,
twitter_consumer_secret = options.twitter_consumer_secret,
xsrf_cookies = False,
autoescape = None,
debug = True,
)
tornado.web.Application.__init__(self, handlers, **settings)
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Lets start hacking !!!")
class AnotherHandler(tornado.web.RequestHandler):
def post(self):
first_arg = self.get_argument("x", None)
second_arg = self.get_argument("y", None)
# some database operation may be ??
# get the data and render
self.render("my_template.html", first_view_arg = first_arg, second_view_arg = second_arg)
def main():
tornado.options.parse_command_line()
logging.getLogger().setLevel(logging.INFO)
http_server = tornado.httpserver.HTTPServer(Application())
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()
I think this approach is better than the first version because you can easily give options and parameters to application object like login path, social api keys, and you can bind other objects to application object and lets them run when the application start.
How to Run Application ?
Yeah I gave you the hello world but did not tell how to run it. Its easy like any other python program.
python yourappname.py
You can also specify the port number on run command.
python yourappname.py --port=9999
How Http Method Response Handling ?
When you wanna send specific Http response code as return you can use built-in set_status(404) which is page not found or set_status(405) which is method not allowed.
class AnotherHandler(tornado.web.RequestHandler):
def post(self):
first_arg = self.get_argument("x", None)
second_arg = self.get_argument("y", None)
if first_arg:
self.set_status(404)
# some database operation may be ??
# get the data and render
self.render("my_template.html", first_view_arg = first_arg, second_view_arg = second_arg)
I think I have covered enough to give you the idea about what is Tornado and how it works. Wait for next post for another topic.