如何在Tornado应用程序中接收多个请求

时间:2016-04-14 11:50:21

标签: python asynchronous tornado

我有一个Tornado Web应用程序,这个应用程序可以从客户端收到GET和POST请求。

POSTs请求将收到的信息放在Tornado队列中,然后我从队列中弹出这些信息,然后用它对数据库进行操作,此操作可能非常慢,可能需要几秒钟才能完成!

在此期间,此数据库操作继续进行,我希望能够接收其他POST(将其他信息放入队列中)和GET。相反,GET非常快,必须立即将结果返回给客户端。

问题是当我从队列中弹出并且开始运行缓慢时,服务器不接受来自客户端的其他请求。我该如何解决这个问题?

这是我到目前为止编写的简化代码(为避免文本墙而省略了导入):

# URLs are defined in a config file
application = tornado.web.Application([
    (BASE_URL, Variazioni),
    (ARTICLE_URL, Variazioni),
    (PROMO_URL, Variazioni),
    (GET_FEEDBACK_URL, Feedback)
])

class Server:

    def __init__(self):
        http_server = tornado.httpserver.HTTPServer(application, decompress_request=True)
        http_server.bind(8889)
        http_server.start(0)

        transactions = TransactionsQueue() #contains the queue and the function with interact with it
        IOLoop.instance().add_callback(transactions.process)

    def start(self):
        try:
            IOLoop.instance().start()
        except KeyboardInterrupt:
            IOLoop.instance().stop()

if __name__ == "__main__":
    server = Server()
    server.start()



class Variazioni(tornado.web.RequestHandler):
    ''' Handle the POST request. Put an the data received in the queue '''

    @gen.coroutine
    def post(self):
        TransactionsQueue.put(self.request.body)
        self.set_header("Location", FEEDBACK_URL)


class TransactionsQueue:
    ''' Handle the queue that contains the data
        When a new request arrive, the generated uuid is putted in the queue
        When the data is popped out, it begin the operation on the database
    '''

    queue = Queue(maxsize=3)

    @staticmethod
    def put(request_uuid):
        ''' Insert in the queue the uuid in postgres format '''
        TransactionsQueue.queue.put(request_uuid)


    @gen.coroutine
    def process(self):
        ''' Loop over the queue and load the data in the database '''
        while True:
            # request_uuid is in postgres format
            transaction = yield TransactionsQueue.queue.get()
            try:
                # this is the slow operation on the database
                yield self._load_json_in_db(transaction )
            finally:
                TransactionsQueue.queue.task_done()

此外我不明白为什么如果我连续5次POST,它会将所有五个数据放入队列,尽管最大值为3。

1 个答案:

答案 0 :(得分:1)

我猜你会使用同步数据库驱动程序,所以_load_json_in_db虽然是协程,但实际上并不是异步。因此,它会阻止整个事件循环,直到长操作完成。这就是为什么服务器在操作完成之前不接受更多请求的原因。

由于_load_json_in_db会阻止事件循环,因此Tornado在运行时无法接受更多请求,因此您的队列永远不会增长到最大值。

您需要两个修复程序。

首先,使用专为Tornado编写的异步数据库驱动程序,或使用Tornado的ThreadPoolExecutor在线程上运行数据库操作。

一旦完成,你的应用程序将能够填满队列,所以第二,TransactionsQueue.put必须这样做:

TransactionsQueue.queue.put_nowait(request_uuid)

如果队列中已有3个项目,则会抛出异常,我认为这是你想要的。