Design of asynchronous request and blocking processing using Tornado

ぐ巨炮叔叔 提交于 2019-12-12 04:02:57

问题


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. 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.

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:

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?

2) What's the best practice for implementing a consumer/producer scheme that is always listening? I'm afraid my while True: statement is actually blocking the processor...

3) How can I inspect the Queue to make sure a burst of calls is being enqueued? I've tried using Queue().qSize(), but it always returns zero, which makes me wonder if the enqueuing is done correctly or not.


回答1:


General rule (credits to NYKevin) is:

  • multiprocessing for CPU- and GPU-bound computations.
  • Event-driven stuff for non-blocking I/O (which should be preferred over blocking I/O where possible, since it scales much more effectively).
  • Threads for blocking I/O (you can also use multiprocessing, but the per-process overhead probably isn't worth it).

ThreadPoolExecutor for IO, ProcessPoolExecutor for CPU. Both have internal queue, both scale to at most specified max_workers. More info about concurrent executors in 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 e.g. some yielded async calls (even yield gen.sleep(0.01)), it gives back control to 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.


来源:https://stackoverflow.com/questions/34745117/design-of-asynchronous-request-and-blocking-processing-using-tornado

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