Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Tornado in 1 Hour (or Less)
Search
Kārlis Lauva
December 04, 2013
Programming
200
4
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Tornado in 1 Hour (or Less)
Tornado crash-course
Kārlis Lauva
December 04, 2013
More Decks by Kārlis Lauva
See All by Kārlis Lauva
Let's talk about PureScript
karlis
0
88
Going Full Monty with full.monty
karlis
1
110
The Transatlantic Struggle
karlis
0
84
Two Scoops of Scala
karlis
0
120
Valsts pārvaldes atvērto datu semantiskās integrācijas procesi
karlis
1
200
Other Decks in Programming
See All in Programming
dRuby over BLE
makicamel
2
380
気圧・高度・GPSを記録&可視化するアプリ「Koudo」を作った話
hjmkth
1
290
キャリア迷子上等 ─ "ない道"は自分で作ればいい
16bitidol
3
2.2k
代数的データ型って何が嬉しいの? #frontend_phpcon_do
kajitack
8
3.7k
AIで効率化できた業務・日常
ochtum
0
140
C# and C++ Interoperability - cho-dotnetnew
harukasao
0
280
Make SRE Operations Easier with Azure SRE Agent
kkamegawa
0
6.9k
エージェンティックRAGにAWSで入門しよう!
har1101
8
1.7k
ローカルLLMを使ってB2Bサービスを作っていての学び
yaotti
0
200
Signal Forms: Details & Live Coding @enterJS 2026 in Mannheim
manfredsteyer
PRO
0
160
New "Type" system on PicoRuby
pocke
1
980
Mujeres en SEO Summit 2026 - Greatest Disaster Hits en Web Performance
guaca
0
190
Featured
See All Featured
KATA
mclloyd
PRO
35
15k
Speed Design
sergeychernyshev
33
1.9k
世界の人気アプリ100個を分析して見えたペイウォール設計の心得
akihiro_kokubo
PRO
71
40k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
310
Odyssey Design
rkendrick25
PRO
2
700
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.2k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.2k
WENDY [Excerpt]
tessaabrams
11
38k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.1k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.8k
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
62
54k
Transcript
TORNADO IN 1 HOUR (OR LESS) Kārlis Lauva @skazhy python.lv
meetup 2013.12.04
Tornado in 3 bullet points • High performance™ web framework
• Async networking library • Gets out of your way (most of the time)
Timeline • 2007.10 - FriendFeed is launched • 2009.08 -
Facebook acquires FriendFeed • 2009.09 - Tornado is open sourced • 2013.09 - Last stable release (3.1.1)
Who’s using it? …..and your company.
Installing pip install tornado pip install git+https://github.com/facebook/tornado.git
“Hello, World!” import tornado.web import tornado.ioloop class Handler(tornado.web.RequestHandler): def get(self):
self.finish("Hello World!") handlers = [tornado.web.URLSpec(r"/$", Handler)] app = tornado.web.Application(handlers) app.listen(1337) tornado.ioloop.IOLoop.instance().start()
MAIN INGREDIENTS
tornado.ioloop • I/O loop for non-blocking sockets • One per
application (or thread) • Uses epoll or kqueue
Example - TCP server def connection_ready(sock, fd, events): while True:
try: connection, address = sock.accept() except socket.error: return connection.setblocking(0) connection.send("Hello") sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.setblocking(0) sock.bind(("", 1337)) sock.listen(128) io_loop = ioloop.IOLoop.instance() callback = functools.partial(connection_ready, sock) io_loop.add_handler(sock.fileno(), callback, io_loop.READ) io_loop.start()
tornado.httpserver • Async web server • Utilizes a singleton IOLoop
• Subclass of TCP server (duh)
Example - HTTP server import tornado.httpserver import tornado.ioloop def handle_request(request):
message = "Hello world\n" request.write("HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s" % \ (len(message), message)) request.finish() http_server = tornado.httpserver.HTTPServer(handle_request) http_server.listen(8080) tornado.ioloop.IOLoop.instance().start()
tornado.web.Application • A collection of resource - handler mappings •
Setting container • Manages UI modules & rendering • Wraps tornado.httpserver • Supports virtual hosts
tornado.web.RequestHandler • Basic building block • If method exists -
call it, else raise HTTP 405 • Not limited to “standard” verbs • Unified request preparation / finalizing methods
A (blocking) request handler # in handler listing URLSpec(r"/users/(\w+)$", SomeHandler)
class Somehandler(tornado.web.RequestHandler): def post(self, user_id): if not self.get_argument(message): raise HTTPError(400, "No message") payload = { "user_id": user_id, "message": self.get_argument(message), } self.application.db.messages.insert(payload) response = HTTPClient().fetch("http://foo.bar.baz") self.render("template.html", {"data": response})
What else is there? • HTTP client • Templating system
• Option management / collection system • Generator interface for async calls • Auth mixins for OAuth • ...and more
ADDING ASYNC-SAUCE
Blocking request redux def post(self, user_id): if not self.get_argument(message): raise
HTTPError(400, "No message") payload = { "user_id": user_id, "message": self.get_argument(message), } self.application.db.messages.insert(payload) response = HTTPClient().fetch("http://foo.bar.baz") self.render("template.html", {"data": response})
Non-blocking request with callback @web.asynchronous def post(self, user_id): def callback(response):
self.render("template.html", {"data": response}) if not self.get_argument(message): raise HTTPError(400, "No message") payload = { "user_id": user_id, "message": self.get_argument(message), } self.application.db.messages.insert(payload) AsyncHTTPClient().fetch("http://foo.bar.baz", callback)
A non-blocking request with @gen @web.asynchronous @gen.engine def post(self, user_id):
def callback(response): if not self.get_argument(message): raise HTTPError(400, "No message") payload = { "user_id": user_id, "message": self.get_argument(message), } self.application.db.messages.insert(payload) resp = yield gen.Task(AsyncHTTPClient().fetch, "http://foo.bar.baz") self.render("template.html", {"data": resp})
Non-blocking calls for pymongo @gen.engine @web.asynchronous def post(self, user_id): def
callback(response): if not self.get_argument(message): raise HTTPError(400, "No message") payload = { "user_id": user_id, "message": self.get_argument(message), } motor.Op(self.application.db.messages.insert, payload) resp = yield gen.Task(AsyncHTTPClient().fetch, "http://foo.bar.baz") self.render("template.html", {"data": resp})
Caveats • Always raise in main thread • Don’t forget
to add decorators • @gen.Tasks are toxic
tornado.websocket class EchoHandler(websocket.WebSocketHandler): def open(self): # the same as handler.get()
logging.debug("WebSocket opened") def on_message(self, message): self.write_message(message) def on_close(self): logging.debug("WebSocket closed") // Client side var ws = new WebSocket("ws://localhost:1337/echo"); ws.onopen = function() { ws.send("Hello, world"); }; ws.onmessage = function (evt) { alert(evt.data); };
TORNADO TEMPLATING
• Django & Jinja2-esque • Easily extendible • Built-in helpers
for reverse urls, etc tornado.template
Example context = { "bugs": {"123": "Fix color scheme"} "uppercase":
lambda x: x.upper() } <ul> {% for bug_id, description in bugs.items() %} <li>{{ bug_id }} - {{ description }}</li> <p>{{ shout(“whoa”) }}</p> {% end %} </li>
TESTING + DEBUGGING
tornado.testing • Wrappers for unittest.TestCase • Helpers for testing async
handlers
PDB • Works as you'd expect app.listen(options.port) pdb.set_trace() tornado.ioloop.IOLoop.instance().start()
REAL WORLD TORNADO
Managing instances with supervisord [program:app-8000] directory=/srv/app/ command=python server.py --port=8101 --mongodb_host=localhost:27017
user=nginx stdout_logfile=/srv/app/logs/app.8000.log [program:app-8001] directory=/srv/app/ command=python server.py --port=8001 --mongodb_host=localhost:27017 user=nginx stdout_logfile=/srv/app/logs/app.8001.log [group:webapp] programs=app-8000,app-8001
Load balancing with nginx upstream webapp { server localhost:8000; server
localhost:8001; } server { server_name foobar; location / { proxy_pass http://webapp; } }
Utilize ngnix to it's full power • Do not serve
static files from Tornado • Whitelist routing (don't bug Tornado with 403s)
Load balancing while you load balance with Amazon ELB
Deployment / production settings python server.py --port=8001 --mongodb_host=localhost:27017 # In
application file from tornado.options import define, options define("port", default=1337, type=int) define("mongodb_host", default="localhost:27017", type=str) define("version", default="1", type=str) class Application(tornado.web.Application): def __init__(self): self.db = MongoClient(options.mongodb_host) handlers = [....] super(Application, self).__init__(handlers, version=options.version) def main(): options.parse_command_line() app = Application() application.listen(options.port)
Tornado and HTTPS • As easy as adding an option
to the application instance ssl_options={ "certfile": path_to_certfile, "keyfile": path_to_keyfile, } app = Application(handlers, ssl_options=ssl_options)
Error collection • Override RequestHandler.send_error() • Use error collection mixins
Sentry integration with Raven • Using mixins to capture exceptions
class Hadler(SentryMixin, tornado.web.RequestHandler): @tornado.web.asynchronous @tornado.gen.engine def get(self): try: raise ValueError() except Exception as e: response = yield tornado.gen.Task( self.captureException, exc_info=True ) self.finish()
Where to go next? • Read through the source •
Start building rad hacks • ??? • PROFIT!!
QUESTION TIME!