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中运行阻止代码