繁体   English   中英

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

[英]Why does an async consumer called in __init__ in a Tornado RequestHandler behave differently from statically called?

我正在尝试使用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)在将来没有阻塞。 即使经过大量实验,我也无法解释这种行为。 我真的很感谢您的帮助。

第一个应用程序只有一个正在运行的consumer因为它只执行一次。 每个请求都“阻塞”使用者(一次只有一个循环),因此下一个将在前一个之后处理。

后一个应用程序对每个请求启动新的consumer循环(因为每个请求均创建了RequestHandler)。 因此,第一个请求不会“阻止”下一个请求,“因为在getsubmit while Truewhile True ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM