Skip to content

Instantly share code, notes, and snippets.

@jmsdnns
Last active December 9, 2017 21:13
Show Gist options
  • Save jmsdnns/1839377546850aaefefd40f4cb099e84 to your computer and use it in GitHub Desktop.
Save jmsdnns/1839377546850aaefefd40f4cb099e84 to your computer and use it in GitHub Desktop.
Examples of blocking / nonblocking code with Tornado
#!/usr/bin/env python
"""
The best way to observe the effects of handler design with respect to blocking
or nonblocking calls is to open two browser tabs and observe the way handling
one request affects the other.
The `TakeFiveHandler` is designed to respond immediately, with a nod to Dave
Brubeck. If you load the `ChillHandler`, which goes to sleep for 15 seconds,
you can see the response time for loading the `TakeFiveHandler` is still
immediate. However, if you load the `SleepHandler` and attempt to load the
`TakeFiveHandler`, you will see that Tornado does not respond until the
`SleepHandler` request is fulfilled. It is blocked by the call to `time.sleep`.
Further down we see use of Tornado's ThreadPool, based on Python futures, which
wraps a thread inside a coroutine and provides a facility for putting blocking
calls inside a nonblocking system.
"""
import os
import time
import tornado.concurrent
import tornado.httpserver
import tornado.ioloop
import tornado.gen
import tornado.options
import tornado.web
FILENAME = "some_large_file"
class TakeFiveHandler(tornado.web.RequestHandler):
def get(self):
self.write("Take five")
class ChillHandler(tornado.web.RequestHandler):
@tornado.gen.coroutine
def get(self):
yield tornado.gen.sleep(15)
self.write("Chill")
class SleepHandler(tornado.web.RequestHandler):
def get(self):
time.sleep(15)
self.write("Sleep")
executor = tornado.concurrent.futures.ThreadPoolExecutor(8)
class ThreadSleepHandler(tornado.web.RequestHandler):
@tornado.gen.coroutine
def get(self):
for i in range(5):
print(i)
yield executor.submit(time.sleep, 3)
self.write("ThreadSleep {}<br/>".format(i))
@tornado.web.stream_request_body
class StreamingSleepHandler(tornado.web.RequestHandler):
@tornado.gen.coroutine
def get(self):
for i in range(5):
print(i)
yield executor.submit(time.sleep, 3)
self.write("StreamingSleep {}<br/>".format(i))
self.flush()
@tornado.web.stream_request_body
class StreamingFileHandler(tornado.web.RequestHandler):
CHUNK_SIZE = 512000
@tornado.gen.coroutine
def get(self):
self.set_header("Content-Type", "text/plain")
self.set_header("Content-Length", os.path.getsize(FILENAME))
self._fd = open(FILENAME, "rb")
yield tornado.gen.Task(self.flush)
while True:
chunk = self._fd.read(self.CHUNK_SIZE)
if chunk:
self.write(chunk)
yield tornado.gen.Task(self.flush)
else:
self._fd.close()
raise tornado.gen.Return()
# App Config
application = tornado.web.Application([
(r"/", TakeFiveHandler),
(r"/chill", ChillHandler),
(r"/sleep", SleepHandler),
(r"/sleep", SleepHandler),
(r"/threadsleep", ThreadSleepHandler),
(r"/streamingsleep", StreamingSleepHandler),
(r"/streamingfile", StreamingFileHandler),
])
# Cli Config
tornado.options.define("port", default=8888, help="run on the given port", type=int)
tornado.options.parse_command_line()
# Server Config
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(tornado.options.options.port)
# Tornado's event loop handles it from here
tornado.ioloop.IOLoop.current().start()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment