我有一个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。
答案 0 :(得分:1)
我猜你会使用同步数据库驱动程序,所以_load_json_in_db
虽然是协程,但实际上并不是异步。因此,它会阻止整个事件循环,直到长操作完成。这就是为什么服务器在操作完成之前不接受更多请求的原因。
由于_load_json_in_db
会阻止事件循环,因此Tornado在运行时无法接受更多请求,因此您的队列永远不会增长到最大值。
您需要两个修复程序。
首先,使用专为Tornado编写的异步数据库驱动程序,或使用Tornado的ThreadPoolExecutor
在线程上运行数据库操作。
一旦完成,你的应用程序将能够填满队列,所以第二,TransactionsQueue.put
必须这样做:
TransactionsQueue.queue.put_nowait(request_uuid)
如果队列中已有3个项目,则会抛出异常,我认为这是你想要的。