Last active
December 9, 2017 21:13
-
-
Save jmsdnns/1839377546850aaefefd40f4cb099e84 to your computer and use it in GitHub Desktop.
Examples of blocking / nonblocking code with Tornado
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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