An Introduction to Tornado

30
An Introduction to Tornado Gavin M. Roy CTO myYearbook.com PhillyPug November 2010

description

Given at PhillyPug Nov 2010

Transcript of An Introduction to Tornado

Page 1: An Introduction to Tornado

An Introduction to TornadoGavin M. Roy

CTOmyYearbook.com

PhillyPug November 2010

Page 2: An Introduction to Tornado

Tornado at myYearbook.com

> 12,000 requests/sec across 7 serversCurrency ConnectRedirect EngineNerveStaplr 2Image Upload

Page 3: An Introduction to Tornado

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

Page 4: An Introduction to Tornado

Well Documented

Page 5: An Introduction to Tornado

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

Page 6: An Introduction to Tornado

Key ModulesTake only what you need

Page 7: An Introduction to Tornado

tornado.web

Most development is focused around this moduleMultiple classes used in a web applicationIncludes decorators as well

Page 8: An Introduction to Tornado

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()

Page 9: An Introduction to Tornado

tornado.web.Application

Initialization Options:Route to request handlersDefault hostSettingsTransformsWSGI

Page 10: An Introduction to Tornado

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

Page 11: An Introduction to Tornado

tornado.web.RequestHandler

Extend in most projectsSession HandlingDatabase, Cache ConnectionsLocalization

Implement for your Application

Page 12: An Introduction to Tornado

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

Page 13: An Introduction to Tornado

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()

Page 14: An Introduction to Tornado

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

Page 15: An Introduction to Tornado

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

Page 16: An Introduction to Tornado

tornado.template

Not requiredSimilar to other enginesLimited python exposure in templateFast, extensibleBuilt in to RequestHandler functionality

Page 17: An Introduction to Tornado

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>

Page 18: An Introduction to Tornado

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")}} &copy; {{ datetime.date.today().year }} Poison Pen, LLC</li> </ul> </body></html>

Page 19: An Introduction to Tornado

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>

Page 20: An Introduction to Tornado

tornado.web.UIModule

Reusable widgets across the siteModules instantiated through templatesOne import assigned when Application is instantiated

Page 21: An Introduction to Tornado

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() }}

Page 22: An Introduction to Tornado

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

Page 23: An Introduction to Tornado

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"

Page 24: An Introduction to Tornado

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

Page 25: An Introduction to Tornado

Using Localization in Templates

<html> <body> {{_("Welcome to our site.")}} </body></html>

Page 26: An Introduction to Tornado

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

Page 27: An Introduction to Tornado

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]

Page 28: An Introduction to Tornado

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

Page 29: An Introduction to Tornado

Questions?

Follow me on Twitter @cradBlog: http://gavinroy.comTinman: http://github.com/gmr/TinmanPika: http://github.com/gmr/Pika