简体   繁体   English

使用Tornado设计异步请求和阻塞处理

[英]Design of asynchronous request and blocking processing using Tornado

I'm trying to implement a Python app that uses async functions to receive and emit messages using NATS , using a client based on Tornado. 我正在尝试使用一个基于Tornado的客户端来实现一个使用异步函数来使用NATS接收和发出消息的Python应用程序。 Once a message is received, a blocking function must be called, that I'm trying to implement on a separate thread, to allow the reception and publication of messages to put messages in a Tornado queue for later processing of the blocking function. 收到消息后,必须调用一个阻塞函数,我正在尝试在单独的线程上实现该函数,以允许消息的接收和发布将消息放入Tornado队列中,以供以后处理阻塞函数。

I'm very new to Tornado (and to python multithreading), but after reading several times the Tornado documentation and other sources, I've been able to put up a working version of the code, that looks like this: 我是Tornado(和python多线程)的新手,但是在阅读了好几次Tornado文档和其他资源后,我已经能够提出一个工作版本的代码,如下所示:

import tornado.gen
import tornado.ioloop
from tornado.queues import Queue
from concurrent.futures import ThreadPoolExecutor
from nats.io.client import Client as NATS

messageQueue = Queue()
nc = NATS()
@tornado.gen.coroutine
def consumer():
    def processMessage(currentMessage):
        # process the message ...

    while True:
        currentMessage = yield messageQueue.get()
        try:
            # execute the call in a separate thread to prevent blocking the queue
            EXECUTOR.submit(processMessage, currentMessage)
        finally:
            messageQueue.task_done()

@tornado.gen.coroutine
def producer():
    @tornado.gen.coroutine
    def enqueueMessage(currentMessage):
        yield messageQueue.put(currentMessage)

    yield nc.subscribe("new_event", "", enqueueMessage)

@tornado.gen.coroutine
def main():
    tornado.ioloop.IOLoop.current().spawn_callback(consumer)
    yield producer()

if __name__ == '__main__':
    main()
    tornado.ioloop.IOLoop.current().start()

My questions are: 我的问题是:

1) Is this the correct way of using Tornado to call a blocking function? 1)这是使用龙卷风调用阻塞函数的正确方法吗?

2) What's the best practice for implementing a consumer/producer scheme that is always listening? 2)实施始终在倾听的消费者/生产者方案的最佳实践是什么? I'm afraid my while True: statement is actually blocking the processor... 恐怕我的while True:语句实际上阻塞了处理器...

3) How can I inspect the Queue to make sure a burst of calls is being enqueued? 3)如何检查队列以确保一阵呼叫入队? I've tried using Queue().qSize(), but it always returns zero, which makes me wonder if the enqueuing is done correctly or not. 我尝试使用Queue()。qSize(),但它始终返回零,这使我想知道入队是否正确完成。

General rule (credits to NYKevin) is: 一般规则(归功于NYKevin)为:

  • multiprocessing for CPU- and GPU-bound computations. 用于CPU和GPU绑定计算的多处理。
  • Event-driven stuff for non-blocking I/O (which should be preferred over blocking I/O where possible, since it scales much more effectively). 非阻塞I / O的事件驱动内容(在可能的情况下,它应优先于阻塞I / O,因为它可以更有效地扩展)。
  • Threads for blocking I/O (you can also use multiprocessing, but the per-process overhead probably isn't worth it). 用于阻止I / O的线程(您也可以使用多处理,但是单进程的开销可能不值得)。

ThreadPoolExecutor for IO, ProcessPoolExecutor for CPU. 用于IO的ThreadPoolExecutor,用于CPU的ProcessPoolExecutor。 Both have internal queue, both scale to at most specified max_workers . 两者都有内部队列, max_workers扩展到最多指定的max_workers More info about concurrent executors in docs . 有关docs中并发执行程序的更多信息。

So answer are: 所以答案是:

  1. Reimplementing pool is an overhead. 重新实现池是一项开销。 Thread or Process depends on what you plan to do. 线程或进程取决于您打算做什么。
  2. while True is not blocking if you have eg some yielded async calls (even yield gen.sleep(0.01) ), it gives back control to ioloop 如果您进行了某些产生异步调用(即使yield gen.sleep(0.01) ), while True不会阻止,它会将控制权交还给ioloop
  3. qsize() is the right to call, but since I have not run/debug this and I would take a different approach (existing pool), it is hard to find a problem here. qsize()是调用权,但是由于我还没有运行/调试它,并且我将采用其他方法(现有池),因此在这里很难找到问题。

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

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