Python tornado gen.coroutine阻止请求

时间:2016-10-16 12:28:21

标签: python tornado python-3.5

我是龙卷风和蟒蛇的新手。几天前我开始写一个非阻塞的休息api,但我还没能完成任务。当我同时向此端点“localhost:8080 / async”发送两个请求时,第二个请求在20秒后响应!这说明我做错了什么。

MAX_WORKERS = 4
class ASYNCHandler(tornado.web.RequestHandler):
    executor = ThreadPoolExecutor(max_workers=MAX_WORKERS)
    counter = 0

    def pow_task(self, x, y):
        time.sleep(10)
        return pow(x,y)

    async def background_task(self):
        future = ASYNCHandler.executor.submit(self.pow_task, 2, 3)
        return future

    @gen.coroutine
    def get(self, *args, **kwargs):
        future = yield from self.background_task()
        response=  dumps({"result":future.result()}, default=json_util.default)
        print(response)


application = tornado.web.Application([
        ('/async', ASYNCHandler),
        ('/sync', SYNCHandler),
    ], db=db, debug=True)  
application.listen(8888)
tornado.ioloop.IOLoop.current().start()

2 个答案:

答案 0 :(得分:0)

永远不要在Tornado代码中使用time.sleep!使用IOLoop.add_timeout稍后或在协程yield gen.sleep(n)中安排回调。

http://www.tornadoweb.org/en/latest/faq.html#why-isn-t-this-example-with-time-sleep-running-in-parallel

答案 1 :(得分:0)

奇怪的是,返回ThreadPoolExecutor未来,基本上阻止了龙卷风的事件循环。如果来自龙卷风团队的任何人都读到这个并知道原因,那么他们能解释一下吗?我曾计划在龙卷风中做一些线程,但在处理完这个问题后,我发现它不会像我原先预料的那样简单。在任何情况下,这里的代码都符合您的预期(我已经将您的原始示例缩小了一点,以便任何人都可以快速运行它):

from concurrent.futures import ThreadPoolExecutor
from json import dumps
import time
from tornado.platform.asyncio import to_tornado_future
from tornado.ioloop import IOLoop
from tornado import gen, web

MAX_WORKERS = 4

class ASYNCHandler(web.RequestHandler):
    executor = ThreadPoolExecutor(max_workers=MAX_WORKERS)
    counter = 0

    def pow_task(self, x, y):
        time.sleep(5)
        return pow(x,y)

    async def background_task(self):
        future = self.executor.submit(self.pow_task, 2, 3)
        result = await to_tornado_future(future)    # convert to tornado future
        return result

    @gen.coroutine
    def get(self, *args, **kwargs):
        result = yield from self.background_task()
        response = dumps({"result": result})
        self.write(response)


application = web.Application([
        ('/async', ASYNCHandler),
    ], debug=True)
application.listen(8888)
IOLoop.current().start()

主要区别在于background_tasks()方法。我将asyncio future转换为tornado future,等待结果,然后返回结果。您在问题中提供的代码,由于某种原因在background_task()屈服时被阻止,而您无法await结果,因为未来不是龙卷风的未来。

稍微不同的是,这个简单的示例可以使用单线程/异步设计轻松实现,并且您的代码也可以在没有线程的情况下完成。线程易于实现,但同样容易出错,并且可能导致非常棘手的情况。在尝试编写线程代码时,请记住this photo:)