Running blocking code in Tornado
I have a tornado app and I want to to use a blocking library to accomplish something. In cases where it's not possible to rewrite the library in an async manner, what's the way to execute it in tornado?
For example, I'd like to be able to put an @asynchronous
decorator on a request handler, in it start some long running function that will just return a response once it's done. I can't just put a callback. The easiest example is of course what is the right way to sleep for 10 seconds without blocking tornado's only thread?
It seems like what I wanted was simply creating a new thread/process, and that the actual act of calling back to tornado needs to be done with IOLoop.instance().add_callback
More information is available here
The code the accepted answer refers to is available on SO.
Another approach is detailed in a blog post here, along with a full working gist. Here is a re-posting of the code from the gist:
from concurrent.futures import ThreadPoolExecutor
from functools import partial, wraps
import time
import tornado.ioloop
import tornado.web
EXECUTOR = ThreadPoolExecutor(max_workers=4)
def unblock(f):
@tornado.web.asynchronous
@wraps(f)
def wrapper(*args, **kwargs):
self = args[0]
def callback(future):
self.write(future.result())
self.finish()
EXECUTOR.submit(
partial(f, *args, **kwargs)
).add_done_callback(
lambda future: tornado.ioloop.IOLoop.instance().add_callback(
partial(callback, future)))
return wrapper
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world %s" % time.time())
class SleepHandler(tornado.web.RequestHandler):
@unblock
def get(self, n):
time.sleep(float(n))
return "Awake! %s" % time.time()
class SleepAsyncHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
def get(self, n):
def callback(future):
self.write(future.result())
self.finish()
EXECUTOR.submit(
partial(self.get_, n)
).add_done_callback(
lambda future: tornado.ioloop.IOLoop.instance().add_callback(
partial(callback, future)))
def get_(self, n):
time.sleep(float(n))
return "Awake! %s" % time.time()
application = tornado.web.Application([
(r"/", MainHandler),
(r"/sleep/(d+)", SleepHandler),
(r"/sleep_async/(d+)", SleepAsyncHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
下面试试这个例子。
import tornado.ioloop
import tornado.web
import time
class MainHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
def get(self, request):
if request is None:
self.application.go = False
self.write("Waiting for GET @ http://localhost:8888/go...<br>")
self.flush()
self._do_wait()
else:
self.application.go = True
self.finish('Thanks!')
def _do_wait(self, timeout_trys=10):
if self.application.go:
self.write('Finish')
self.finish()
else:
self.write("Sleeping 2 second, timeout_trys=%s<br>" % timeout_trys)
self.flush()
tornado.ioloop.IOLoop.instance().add_timeout(time.time() + 2,
lambda: self._do_wait(timeout_trys-1))
application = tornado.web.Application([
(r"/(w+)?", MainHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
链接地址: http://www.djcxy.com/p/27848.html
上一篇: 作为AsyncHTTPClient接收块的Tornado流HTTP响应
下一篇: 在Tornado中运行阻止代码