为什么在Tornado RequestHandler中__init__中调用的异步使用者的行为与静态调用的行为不同?

时间:2017-10-21 21:00:05

标签: python asynchronous tornado consumer

我正在尝试使用Tornado为每个处理程序创建一个具有唯一队列的异步服务器。调用端点时,作业将放入队列中。我有一个消费者功能,它从队列中异步“消耗”作业。但是,消费者的行为往往会有所不同,具体取决于我是将其称为self.consumer()还是AsyncHandler.consumer()。我最初的猜测是,由于实例级别锁定,但无法找到它的证据。我连续发了4个帖子请求。以下是带有输出的2个片段。

import tornado.web
from tornado import gen
from time import sleep, time
from tornado.queues import Queue
from concurrent.futures import ThreadPoolExecutor
from tornado.ioloop import IOLoop

class AsyncHandler(tornado.web.RequestHandler):

    JOB_QUEUE = Queue()
    EXECUTOR = ThreadPoolExecutor()

    def post(self):
        job = lambda: sleep(3) or print("{}:handler called".format(int(time())))
        self.JOB_QUEUE.put(job)
        self.set_status(200)
        self.finish()

    @staticmethod
    @gen.coroutine
    def consumer():
        while True:
            job = yield AsyncHandler.JOB_QUEUE.get()
            print("qsize : {}".format(AsyncHandler.JOB_QUEUE.qsize()))
            print(AsyncHandler.JOB_QUEUE)
            output = yield AsyncHandler.EXECUTOR.submit(job)
            AsyncHandler.JOB_QUEUE.task_done()


if __name__ == "__main__":
    AsyncHandler.consumer()
    APP = tornado.web.Application([(r"/test", AsyncHandler)])
    APP.listen(9000)
    IOLoop.current().start()

这给出了预期的输出:

qsize : 0
<Queue maxsize=0 tasks=1>
1508618429:handler called
qsize : 2
<Queue maxsize=0 queue=deque([<function...<lambda> at 0x7fbf8f741400>, <function... <lambda> at 0x7fbf8f760ea0>]) tasks=3>
1508618432:handler called
qsize : 1
<Queue maxsize=0 queue=deque([<function AsyncHandler.post.<locals>.<lambda> at 0x7fbf8f760ea0>]) tasks=2>
1508618435:handler called
qsize : 0
<Queue maxsize=0 tasks=1>
1508618438:handler called

output = yield AsyncHandler.EXECUTOR.submit(job)需要3秒才能返回输出,因此输出延迟3秒。此外,我们可以看到队列同时建立起来。

现在来看一段有趣的代码:

import tornado.web
from tornado import gen
from time import sleep, time
from tornado.queues import Queue
from concurrent.futures import ThreadPoolExecutor
from tornado.ioloop import IOLoop

class AsyncHandler(tornado.web.RequestHandler):
    JOB_QUEUE = Queue()
    EXECUTOR = ThreadPoolExecutor()

    def __init__(self, application, request, **kwargs):
        super().__init__(application, request, **kwargs)
        self.consumer()

    def post(self):
        job = lambda: sleep(3) or print("{}:handler called".format(int(time())))
        self.JOB_QUEUE.put(job)
        self.set_status(200)
        self.finish()

    @staticmethod
    @gen.coroutine
    def consumer():
        while True:
            job = yield AsyncHandler.JOB_QUEUE.get()
            print("qsize : {}".format(AsyncHandler.JOB_QUEUE.qsize()))
            print(AsyncHandler.JOB_QUEUE)
            output = yield AsyncHandler.EXECUTOR.submit(job)
            AsyncHandler.JOB_QUEUE.task_done()


if __name__ == "__main__":
    APP = tornado.web.Application([(r"/test", AsyncHandler)])
    APP.listen(9000)
    IOLoop.current().start()

奇怪的(和令人愉快的)输出看起来像:

qsize : 0
<Queue maxsize=0 tasks=1>
qsize : 0
<Queue maxsize=0 tasks=2>
qsize : 0
<Queue maxsize=0 tasks=3>
qsize : 0
<Queue maxsize=0 tasks=4>
1508619138:handler called
1508619138:handler called
1508619139:handler called
1508619139:handler called

请注意,现在我们在__init__内调用消费者。我们可以看到任务并行构建和执行(没有队列构建),几乎同时完成。这就好像output = yield AsyncHandler.EXECUTOR.submit(job)未来阻止了。即使经过大量的实验,我也无法解释这种行为。我真的很感激一些帮助。

1 个答案:

答案 0 :(得分:0)

第一个应用程序只有一个正在运行consumer因为它执行了一次。每个请求“阻止”(一次只有一个是循环)消费者,所以下一个请求将在前一个之后处理。

后一个app会针对每个请求启动一个新的consumer循环(因为每个req创建了RequestHandler)。因此,第一个请求不会“阻止”下一个请求,因为有while Trueget的全新submit ...