0
AN INTRODUCTION TO     TORNADO               Gavin M. Roy                   CTO              myYearbook.compyCon 2011Atlan...
TORNADO AT               MYYEARBOOK.COM• Currency     Connect  • Marketing Website, Portal, RESTful API• Redirect     Engi...
WHAT IS TORNADO?•A scalable, non-blocking web server and micro-framework in Python 2.5 & 2.6.  • Python          3 port un...
FEATURES•   Small barrier to entry to quickly developing applications•   Third Party Authentication via OpenID, OAuth Mixi...
class Application(object):      """A collection of request handlers that make up a web application.      Instances of this...
WHAT TORNADO ISN’T•A   full stack framework like Django• Based   on Twisted • There    is an unmaintained port, Tornado on...
TORNADO VS TWISTED•   Tornado doesn’t have to be asynchronous•   It doesn’t have as many asynchronous drivers    •   Can i...
KEY MODULESTake only what you need
TORNADO.WEB• Most   development is focused around this module• Multiple   classes used in a web application• Includes   de...
TORNADO APPLICATION• tornado.web.Application: Main           controller class• Canonical Tornado      Hello World:     imp...
REQUEST HANDLERS• tornado.web.RequestHandler • Extend   RequestHandler for larger web apps   • Session   Handling   • Data...
REQUEST HANDLERS• Classes   implementing define functions for processing  • get, head, post, delete, put, options• Hooks   ...
REQUEST HANDLER EXAMPLE   import redis   import tornado.web   class MyRequestHandler(tornado.web.RequestHandler):       de...
TORNADO.TEMPLATE• Not   required• Similar    to other engines• Limited    python exposure in template• Fast, extensible• B...
REQUESTHANDLER.RENDERCode           class Home(RequestHandler):               def get(self):                   self.render...
BASE TEMPLATE<html>    <head>        <title>My Site :: {% block title %}Unextended Template{% end %}</title>        <link ...
CONTENT TEMPLATE{% extends "base.html" %}{% block title %}{{_("Error Title")}}{% end %}{% block content %}<h1>{{_("Error T...
TEMPLATE XSRF EXAMPLE <form action="/login" method="post">   {{ xsrf_form_html() }}   <div>Username: <input type="text" na...
UI MODULES• Extend templates with reusable widgets across the site• One import assigned when Application is created• Simil...
UIMODULE EXAMPLE
Embed                  UIMODULE EXAMPLE                  <div>{{ modules.HTTPSCheck() }}</div>                 class HTTPS...
TORNADO.LOCALE•   Locale files in one directory    •   In a csv format    •   Named as locale.csv, e.g.        en_US.csv•  ...
ADDING LOCALIZATION import tornado.locale as locale import tornado.web class RequestHandler(tornado.web.RequestHandler):  ...
USING LOCALIZATION<html>  <body>    {{_("Welcome to our site.")}}  </body></html>
LOCALE FILE EXAMPLE: DE_DE   "New","Neu"   "Donate","Spenden"   "New Paste","Neuer Paste"   "Secure, Private Pasting","Sic...
TEMPLATE EXAMPLE AGAIN<html>    <head>        <title>My Site :: {% block title %}Unextended Template{% end %}</title>     ...
TORNADO.AUTH• Built   in Mixins for OpenID, OAuth, OAuth2  •    Google, Twitter, Facebook, Facebook Graph, Friendfeed• Use...
USING TORNADO.AUTH- [/login/form, site.auth_reg.LoginForm]- [/login/friendfeed, site.auth_reg.LoginFriendFeed]class LoginF...
TORNADO.IOLOOP• Protocol    independent• tornado.httpserver.HTTPServer     uses ioloop.IOLoop• RabbitMQ      driver Pika u...
TORNADO.IOLOOP EXAMPLE  class MyClient(object):      def connect(self, host, port):          # Create our socket          ...
OTHER MODULES OF NOTE•   tornado.database                    •   tornado.options    •   MySQL wrapper                     ...
ASYNC DRIVERS• Memcache• MongoDB• PostgreSQL• RabbitMQ• Redis
FIN• Follow   me on Twitter @crad• Blog: http://gavinroy.com• Pika: http://github.com/pika  • Async   RabbitMQ/AMQP Suppor...
IMAGE CREDITS• Lego by Craig A. Rodway http://www.flickr.com/photos/m0php/530526644/• Delta   Clipper X courtesy of NASA• U...
Upcoming SlideShare
Loading in...5
×

An Introduction to Tornado

7,686

Published on

Published in: Technology
2 Comments
13 Likes
Statistics
Notes
No Downloads
Views
Total Views
7,686
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
112
Comments
2
Likes
13
Embeds 0
No embeds

No notes for slide

Transcript of "An Introduction to Tornado"

  1. 1. AN INTRODUCTION TO TORNADO Gavin M. Roy CTO myYearbook.compyCon 2011Atlanta, GA
  2. 2. TORNADO AT MYYEARBOOK.COM• Currency Connect • Marketing Website, Portal, RESTful API• Redirect Engine• Nerve• Staplr 2• Image Upload Service
  3. 3. WHAT IS TORNADO?•A scalable, non-blocking web server and micro-framework in Python 2.5 & 2.6. • Python 3 port underway• Developed at FriendFeed and open-sourced by Facebook• Similar to web.py in use• Fast: ~1,500 requests/sec backend* * Your milage will vary
  4. 4. FEATURES• Small barrier to entry to quickly developing applications• Third Party Authentication via OpenID, OAuth Mixins• Light-weight template system• Auto-magical cross-site forgery protection• WSGI && Google App Engine Support• Develop using debug mode and automatically reload code and templates when changed on disk
  5. 5. class Application(object):     """A collection of request handlers that make up a web application. Instances of this class are callable and can be passed directly to HTTPServer to serve the application: application = web.Application([ (r"/", MainPageHandler), ]) http_server = httpserver.HTTPServer(application) http_server.listen(8080) ioloop.IOLoop.instance().start() The constructor for this class takes in a list of URLSpec objects or (regexp, request_class) tuples. When we receive requests, we iterate over the list in order and instantiate an instance of the first request class whose regexp matches the request path. Each tuple can contain an optional third element, which should be a dictionary if it is present. That dictionary is passed as keyword arguments to the contructor of the handler. This pattern is used for the StaticFileHandler below: application = web.Application([ (r"/static/(.*)", web.StaticFileHandler, {"path": "/var/www"}), ])CLEAN, WELL DOCUMENTED CODE
  6. 6. WHAT TORNADO ISN’T•A full stack framework like Django• Based on Twisted • There is an unmaintained port, Tornado on Twisted • Influenced the Cyclone project•A replacement for a front-end web server • Run behind a reverse proxy http server (nginx, Cherokee)
  7. 7. TORNADO VS TWISTED• Tornado doesn’t have to be asynchronous• It doesn’t have as many asynchronous drivers • Can introduce blocking behaviors• The Tornado code is smaller and very easy to understand• Less mature than Twisted• You don’t need to buy into a development methodology • Write Python not Twisted
  8. 8. KEY MODULESTake only what you need
  9. 9. TORNADO.WEB• Most development is focused around this module• Multiple classes used in a web application• Includes decorators • Asynchronous function: @tornado.web.asynchronous • Authentication Required: @tornado.web.authenticated
  10. 10. TORNADO APPLICATION• tornado.web.Application: Main controller class• Canonical Tornado Hello World: import tornado.httpserver import tornado.ioloop import 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()
  11. 11. REQUEST HANDLERS• tornado.web.RequestHandler • Extend RequestHandler for larger web apps • Session Handling • Database, Cache Connections • Localization• Implement for your Application
  12. 12. REQUEST HANDLERS• Classes implementing define functions for processing • get, head, post, delete, put, options• Hooks on Initialization, Prepare, Close• Functions for setting HTTP Status, Headers, Cookies, Redirects and more
  13. 13. REQUEST HANDLER EXAMPLE import redis import 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()
  14. 14. TORNADO.TEMPLATE• Not required• Similar to other engines• Limited python exposure in template• Fast, extensible• Built-in support in the RequestHandler class• Adds cache busting static content delivery
  15. 15. REQUESTHANDLER.RENDERCode class Home(RequestHandler): def get(self): self.render(home.html, username=Leeroy Jenkins);Template <html> <body> Hi {{username}}, welcome to our site. </body> </html>
  16. 16. BASE TEMPLATE<html> <head> <title>My Site :: {% block title %}Unextended Template{% end %}</title> <link rel="stylesheet" type="text/css" href="{{ static_url(css/site.css) }}" /> <script type="text/javascript" src="{{ static_url(javascript/site.js) }}"></script> {% 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 }}</li> </ul> </body></html>
  17. 17. CONTENT TEMPLATE{% extends "base.html" %}{% block title %}{{_("Error Title")}}{% end %}{% block content %}<h1>{{_("Error Title")}}</h1><img src="/static/images/sad_robot.png" class="error_robot" /><p class="error_message">{{_("Error Message")}}</p><h2 class="error">{{status_code}} - {{exception}}</h2>{% end %}
  18. 18. 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> No additional work required.
  19. 19. UI MODULES• Extend templates with reusable widgets across the site• One import assigned when Application is created• Similar to RequestHandler in behavior
  20. 20. UIMODULE EXAMPLE
  21. 21. Embed UIMODULE EXAMPLE  <div>{{ modules.HTTPSCheck() }}</div> class HTTPSCheck(tornado.web.UIModule): def render(self):UIModule Class 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 <div class="information">Template <a href="https://{{request.host}}{{request.uri}}"> {{_("Click here to use a secure connection")}} </a> </div>
  22. 22. TORNADO.LOCALE• Locale files in one directory • In a csv format • Named as locale.csv, e.g. en_US.csv• tornado.locale.load_translations (path) • Pass path where files are located• Invoked as _ method in templates
  23. 23. ADDING LOCALIZATION import tornado.locale as locale import 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
  24. 24. USING LOCALIZATION<html> <body> {{_("Welcome to our site.")}} </body></html>
  25. 25. 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"
  26. 26. TEMPLATE EXAMPLE AGAIN<html> <head> <title>My Site :: {% block title %}Unextended Template{% end %}</title> <link rel="stylesheet" type="text/css" href="{{ static_url(css/site.css) }}" /> <script type="text/javascript" src="{{ static_url(javascript/site.js) }}"></script> {% 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 }}</li> </ul> </body></html>
  27. 27. TORNADO.AUTH• Built in Mixins for OpenID, OAuth, OAuth2 • Google, Twitter, Facebook, Facebook Graph, Friendfeed• Use RequestHandler to extend your own login functions with the mixins if wanted• Is asynchronous • Not supported in WSGI and Google App Engine
  28. 28. USING TORNADO.AUTH- [/login/form, site.auth_reg.LoginForm]- [/login/friendfeed, site.auth_reg.LoginFriendFeed]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]
  29. 29. TORNADO.IOLOOP• Protocol independent• tornado.httpserver.HTTPServer uses ioloop.IOLoop• RabbitMQ driver Pika uses ioloop.IOLoop• Built in timer functionality • tornado.ioloop.add_timeout • tornado.ioloop.PeriodicCallback
  30. 30. TORNADO.IOLOOP EXAMPLE class MyClient(object): def connect(self, host, port): # Create our socket 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() # Append our handler to tornados 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/pika/pika/blob/master/pika/adapters/tornado_connection.py
  31. 31. OTHER MODULES OF NOTE• tornado.database • tornado.options • MySQL wrapper • Similar to optparse• tornado.escape • tornado.testing • Misc escape functions • Test support classes• tornado.httpclient • tornado.websocket • Async HTTP client • Websocket Support• tornado.iostream • Non-blocking TCP helper class
  32. 32. ASYNC DRIVERS• Memcache• MongoDB• PostgreSQL• RabbitMQ• Redis
  33. 33. FIN• Follow me on Twitter @crad• Blog: http://gavinroy.com• Pika: http://github.com/pika • Async RabbitMQ/AMQP Support for Tornado• We’re hiring at myYearbook.com • Drop me an email: gmr@myyearbook.com
  34. 34. IMAGE CREDITS• Lego by Craig A. Rodway http://www.flickr.com/photos/m0php/530526644/• Delta Clipper X courtesy of NASA• United Nations San Francisco Conference by Yould http://www.flickr.com/photos/un_photo/3450033473/
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×