Running blocking code in Tornado

心不动则不痛 提交于 2019-12-21 20:23:36

问题


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?


回答1:


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




回答2:


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() 



回答3:


Try this example below.

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()


来源:https://stackoverflow.com/questions/14293238/running-blocking-code-in-tornado

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!