Sie sind auf Seite 1von 23

Tornado Web Server

Bret Taylor
Director of Products
Background
Background
▪ Small, fast, and hackable

▪ Focus on the fundamentals: request handling, performance,


templating, localization, security, and simplicity
▪ Non-blocking I/O to enable streaming and hanging connections (i.e.,
real-time services)
▪ Few dependencies and application assumptions
Hello, world

class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")

application = tornado.web.Application([
(r"/", MainHandler),
])

if __name__ == "__main__":
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Templates
class MainHandler(tornado.web.RequestHandler):
def get(self):
items = ["Item 1", "Item 2", "Item 3"]
title = “My list of items”
self.render("items.html", title=title, items=items)

application = tornado.web.Application([
(r"/", MainHandler),
])

if __name__ == "__main__":
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Templates

<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<ul>
{% for item in items %}
<li>{{ escape(item) }}</li>
{% end %}
</ul>
</body>
</html>
Templates
base.html

<html>
<head>
<title>{% block title %}{% end %}</title>
</head>
<body>
<div id=”header”>Bret’s site</div>
<div id=”body”>
items.html
{% block body %}{% end %}
</div> {% extends “base.html” %}
</body>
</html> {% block title %}{{ title }}{% end %}

{% block body %}
{% for item in items %}
<li>{{ escape(item) }}</li>
{% end %}
{% end %}
UI modules: reusable components

home.html entry.html

<html> <html>
<head> <head>
<title>My blog</title> <title>{{ e.title }}</title>
</head> </head>
<body> <body>
{% for entry in entries %} show entry with comments
show entry without comments </body>
{% end %} </html>
</body>
</html>
UI modules: reusable components
class BlogEntry(tornado.web.UIModule):
def render(self, entry, show_comments=False):
return self.render_string(
“blogentry.html”, show_comments=show_comments)

blogentry.html

<div class=”entry”>
<h1>{{ entry.title }}</h1>
<div class=”body”>{{ entry.body }}</div>
{% if show_comments %}
{% for comment in comments %}
<div class=”comment”>{{ entry.comment }}</div>
{% end %}
{% end %}
</div>
UI modules: reusable components

<html>
<head>
<title>My blog</title>
</head>
<body>
{% for entry in entries %}
{{ modules.BlogEntry(entry, show_comments=False) }}
{% end %}
</body>
</html>
UI modules: reusable components

<div class=”entry”>
<h1>{{ entry.title }}</h1>
<div class=”body”>{{ entry.body }}</div>
{% if show_comments %}
<div id=”disqus_thread”></div>
{% end %}
</div>
UI modules: reusable components
class BlogEntry(tornado.web.UIModule):
def __init__(self):
self.include_disqus = False

def render(self, entry, show_comments=False):


if show_comments: self.include_disqus = True
return self.render_string(
“blogentry.html”, show_comments=show_comments)

def javascript_files(self):
if self.include_disqus:
return “http://disqus.com/forums/embed.js”

...
<script src=”http://disqus.com/...”></script>
</body>
</html>
Secure cookies + authentication

class LoginHandler(tornado.web.RequestHandler):
def post(self):
user = self.check_password(...)
if not user:
self.redirect(“/login?error=1”)
return
self.set_secure_cookie(“uid”, user[“id”])
self.redirect(“/home”)

uid=1213|1253739166|8eb53f1a05f10b5149e7e8147afb7

value timestamp signature


Secure cookies + authentication

class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
uid = self.get_secure_cookie(“uid”)
if not uid: return None
return backend.get_user_by_id(uid)

class HomeHandler(BaseHandler):
@tornado.web.authenticated
def get(self):
messages = backend.get_messages(self.current_user)
self.render(“home.html”, messages=messages)
Secure cookies + authentication

settings = {
"cookie_secret": "61oETzKXQAGaYdkL5gEmGe...",
"login_url": "/login",
}
application = tornado.web.Application([
(r"/", LoggedOutHomeHandler),
(r"/home", HomeHandler),
(r"/login", LoginHandler),
], **settings)
Real-time + non-blocking
GET /ajax/wait_for_updates?cursor=3172

wait for messages or timeout (1, 10, or even 60 seconds) 10,000s of active
hanging connections
in this state

HTTP/1.1 200 OK

{“new_messages”: [{...}, {...}],


“cursor”: 3174}

GET /ajax/wait_for_updates?cursor=3174
Real-time + non-blocking

class MessageUpdatesHandler(BaseHandler):
@tornado.web.asynchronous
def post(self):
cursor = self.get_argument("cursor", None)
self.wait_for_messages(
self.async_callback(self.on_new_messages),
cursor=cursor)

def on_new_messages(self, messages, cursor):


# Tornado automatically makes dicts into JSON
self.write(dict(messages=messages, cursor=cursor))
self.finish()
Third party authentication
class LoginHandler(tornado.web.RequestHandler,
tornado.auth.FacebookMixin):
@tornado.web.asynchronous
def get(self):
if self.get_argument("session", None):
self.get_authenticated_user(
self.async_callback(self._on_auth))
return
self.authenticate_redirect()

def _on_auth(self, user):


if not user: raise tornado.web.HTTPError(500, "Auth failed")
self.set_secure_cookie("uid", user["uid"])
self.set_secure_cookie("session_key", user["session_key"])
self.redirect("/home")
Tornado in production
nginx
(load balancer +
static file server)

Tornado frontends

MySQL + memcached
Asynchronous design style
▪ Blocking operations are not an issue if there's no more than one
concurrent request per server instance
▪ Synchronous style of programming is easier, so don’t async
everything
▪ Fast things (e.g., memcache) don’t need to be multiplexed, slow things
(e.g., HTTP requests to external sites) do
▪ Gray area: database requests
▪ Service-oriented architecture helps mitigate some of these issues
http://www.tornadoweb.org/
(c) 2009 Facebook, Inc. or its licensors.  "Facebook" is a registered trademark of Facebook, Inc.. All rights reserved. 1.0

Das könnte Ihnen auch gefallen