an introduction to tornado
DESCRIPTION
Given at PhillyPug Nov 2010TRANSCRIPT
An Introduction to TornadoGavin M. Roy
CTOmyYearbook.com
PhillyPug November 2010
Tornado at myYearbook.com
> 12,000 requests/sec across 7 serversCurrency ConnectRedirect EngineNerveStaplr 2Image Upload
What is Tornado?
A scalable, non-blocking web server and micro-frameworkDeveloped at FriendFeed and open-sourced by FacebookSimilar to web.py in useFast: 1,500 requests/sec backend** Your milage may and most likely will vary
Well Documented
What Tornado Isn’t
A full stack frameworkBased on Twisted
There is an unmaintained port, Tornado on TwistedInfluenced the Cyclone project
A replacement for a front-end web server
Key ModulesTake only what you need
tornado.web
Most development is focused around this moduleMultiple classes used in a web applicationIncludes decorators as well
tornado.web.Application
Main controller classHello, World:import tornado.httpserverimport tornado.ioloopimport tornado.web
class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world")
if __name__ == "__main__": application = tornado.web.Application([ (r"/", MainHandler), ]) http_server = tornado.httpserver.HTTPServer(application) http_server.listen(8888) tornado.ioloop.IOLoop.instance().start()
tornado.web.Application
Initialization Options:Route to request handlersDefault hostSettingsTransformsWSGI
tornado.web.Application Settings
debug: Reload on save of code and templatesgzip: Enable GZip Content Encodinglogin_url: When using @tornado.web.authenticated decorator static_path: Base path for static assetstemplate_path: Base path for template filesui_modules: “blocks” that plug into templatesxsrf_cookies: Cross-site forgery protection
tornado.web.RequestHandler
Extend in most projectsSession HandlingDatabase, Cache ConnectionsLocalization
Implement for your Application
tornado.web.RequestHandler
Classes implementing define functions for processing
get, head, post, delete, put, optionsHooks on Initialization, Prepare, CloseFunctions for setting HTTP Status, Headers, Cookies, Redirects and more
RequestHandler Example
import redisimport tornado.web
class MyRequestHandler(tornado.web.RequestHandler): def initialize(self):
host = self.application.settings['Redis']['host'] port = self.application.settings['Redis']['port'] self.redis = redis.Redis(host, port)
class Homepage(MyRequestHandler):
@tornado.web.asynchronous def get(self):
content = self.redis.get('homepage') self.write(content) self.finish()
tornado.ioloop
Protocol independenttornado.httpserver.HTTPServer implemented using the ioloopImplemented a Tornado adapter for Pika using itBuilt in timer functionality
tornado.ioloop.add_timeouttornado.ioloop.PeriodicCallback
tornado.ioloop Example
class MyServer(object):
def connect(self, host, port):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) self.sock.connect((host, port)) self.sock.setblocking(0) self.io_loop = tornado.ioloop.IOLoop.instance() self.handle_connection_open() # Append our handler to tornado's ioloop for our socket events = tornado.ioloop.IOLoop.READ | tornado.ioloop.IOLoop.ERROR
self.io_loop.add_handler(self.sock.fileno(), self._handle_events, events)
https://github.com/gmr/pika/blob/master/pika/tornado_adapter.py
tornado.template
Not requiredSimilar to other enginesLimited python exposure in templateFast, extensibleBuilt in to RequestHandler functionality
RequestHandler.render
class Home(RequestHandler):
def get(self):
self.render('home.html', username='Leeroy Jenkins');
<html> <body> Hi {{username}}, welcome to our site. </body></html>
Template Example
<html> <head> <title>eMuse :: {% block title %}Unextended Template{% end %}</title> <link rel="stylesheet" type="text/css" href="{{static_url('css/site.css')}}" />{% block css %}{% end %} <script type="text/javascript" src="{{static_url('javascript/site.js')}}"></script> {% block javascript %}{% end %} {% if not current_user %}<script type="text/javascript" src="http://api.recaptcha.net/js/recaptcha_ajax.js"></script>{% end %} </head> <body{% if current_user %} class="authenticated"{% end %}> {% include "header.html" %} {% if request.uri not in ['/', ''] and current_user %} {{modules.MemberBar()}} {% end %} <div id="content"> {% block content %} No Content Specified {% end %} </div> <ul id="footer"> <li><a href="/terms">{{_("Terms and Conditions")}}</a></li> <li class="center">{{_("Version")}}: {{handler.application.settings['version']}}</li> <li class="right">{{_("Copyright")}} © {{ datetime.date.today().year }} Poison Pen, LLC</li> </ul> </body></html>
Template XSRF Example
<form action="/login" method="post"> {{ xsrf_form_html() }} <div>Username: <input type="text" name="username"/></div> <div>Password: <input type="password" name="password"/></div> <div><input type="submit" value="Sign in"/></div></form>
tornado.web.UIModule
Reusable widgets across the siteModules instantiated through templatesOne import assigned when Application is instantiated
UIModule Example
class HTTPSCheck(tornado.web.UIModule):
def render(self):
if 'X-Forwarded-Ssl' not in self.request.headers or \ self.request.headers['X-Forwarded-Ssl'] != 'on': return self.render_string("modules/ssl.html") return ''
<li class="information"> <a href="https://{{request.host}}{{request.uri}}"> {{_("Click here to use a secure connection")}} </a></li>
{{ modules.HTTPSCheck() }}
tornado.locale
Locale files in one directoryIn a csv formatNamed for locale.csv, e.g. en_US.csv
tornado.locale.load_translationsPass in directory where files are located
tornado.locale.get_supported_locales
Locale File Example: de_DE
"New","Neu""Donate","Spenden""New Paste","Neuer Paste""Secure, Private Pasting","Sicheres Pasten""Unclaimed Hostname","Sie benutzen einen offenen Hostnamen. Klicken Sie heir für weitere Informationen.""Paste Options","Paste Optionen""Formatting","Formatierung""No Formatting","Keine Formatierung""Line Numbers","Zeilennummern""On","An""Off","Aus""Minutes","Minuten""Hour","Stunde""Day","Tag""Week","Woche""Year","Jahr""Expire Paste","Wann soll der Paste gelöscht werden?""Encryption","Verschlüsselung""Encryption Key","Passwort-Verschlüsselung""Encryption Algorithm","Algorithm-Verschlüsselung""Save Paste","Paste speichern""All Rights Reserved","Alle Rechte vorbehalten"
Using Localization
import tornado.locale as localeimport tornado.web
class RequestHandler(tornado.web.RequestHandler):
def get_user_locale(self): # Fake user object has a get_locale() function user_locale = self.user.get_locale() # If our locale is supported return it if user_locale in locale.get_supported_locales(None): return user_locale # Defaults to Accept-Language header if supported return None
Using Localization in Templates
<html> <body> {{_("Welcome to our site.")}} </body></html>
tornado.auth
Built in OAuth Mixins for Google, Twitter, Facebook, FriendfeedUse RequestHandler to extend your own Login functions with the mixins if wantedBuilt to be asynchronous
Using tornado.auth
class LoginFriendFeed(RequestHandler, tornado.auth.FriendFeedMixin):
@tornado.web.asynchronous def get(self): if self.get_argument("oauth_token", None): self.get_authenticated_user(self.async_callback(self._on_auth)) return self.authorize_redirect()
def _on_auth(self, ffuser): if not ffuser: raise tornado.web.HTTPError(500, "FriendFeed auth failed") return
username = ffuser['username']
- [/login/form, emuse.auth_reg.LoginForm]- [/login/friendfeed, emuse.auth_reg.LoginFriendFeed]
Other Modules of Note
tornado.database
MySQL wrapper
tornado.escape
Misc escape functions
tornado.httpclient
Async HTTP client
tornado.iostream
Non-blocking TCP helper class
tornado.options
Similar to optparse
tornado.testing
Test support classes
tornado.wsgi
WSGI Support
tornado.websocket
Websocket Support
Questions?
Follow me on Twitter @cradBlog: http://gavinroy.comTinman: http://github.com/gmr/TinmanPika: http://github.com/gmr/Pika
Image Credits
Lego by Craig A. Rodwayhttp://www.flickr.com/photos/m0php/530526644/