Tornado 6.1 非阻塞请求
Posted
技术标签:
【中文标题】Tornado 6.1 非阻塞请求【英文标题】:Tornado 6.1 non-blocking request 【发布时间】:2021-08-28 08:31:06 【问题描述】:使用 Tornado,我有一个需要很长时间的 POST 请求,因为它向另一个 API 服务发出许多请求并处理数据。这可能需要几分钟才能完全完成。我不希望这会阻止整个 Web 服务器响应它当前所做的其他请求。
我在 SO 上查看了多个线程,但它们通常已有 8 年历史,并且由于 tornado 从 tornado.gen 中删除了“引擎”组件,因此代码不再工作。
有没有一种简单的方法可以启动这个漫长的 get 调用,并且不会让它在此过程中阻塞整个 Web 服务器?有什么我可以在代码中说的......“提交 POST 响应并处理这一功能,而不会阻止任何并发服务器请求获得即时响应”?
示例: main.py
def make_app():
return tornado.web.Application([
(r"/v1", MainHandler),
(r"/v1/addfile", AddHandler, dict(folderpaths = folderpaths)),
(r"/v1/getfiles", GetHandler, dict(folderpaths = folderpaths)),
(r"/v1/getfile", GetFileHandler, dict(folderpaths = folderpaths)),
])
if __name__ == "__main__":
app = make_app()
sockets = tornado.netutil.bind_sockets(8888)
tornado.process.fork_processes(0)
tornado.process.task_id()
server = tornado.httpserver.HTTPServer(app)
server.add_sockets(sockets)
tornado.ioloop.IOLoop.current().start()
addHandler.py
class AddHandler(tornado.web.RequestHandler):
def initialize(self, folderpaths):
self.folderpaths = folderpaths
def blockingFunction(self):
time.sleep(320)
post("AWAKE")
def post(self):
user = self.get_argument('user')
folderpath = self.get_argument('inpath')
outpath = self.get_argument('outpath')
workflow_value = self.get_argument('workflow')
status_code, status_text = validateInFolder(folderpath)
if (status_code == 200):
logging.info("Status Code 200")
result = self.folderpaths.add_file(user, folderpath, outpath, workflow_value)
self.write(result)
self.finish()
#At this point the path is validated.
#POST response should be send out. Internal process should continue, new
#requests should not be blocked
self.blockingFunction()
想法是,如果输入参数得到验证,则应该发送 POST 响应。 然后应该启动内部进程 (blockingFunction()),它不应该阻止 Tornado 服务器处理另一个 API POST 请求。
我尝试将 (blockingFunction()) 定义为异步,这允许我处理多个并发用户请求 - 但是有一个关于异步方法缺少“等待”的警告。
欢迎任何帮助。谢谢
【问题讨论】:
使用run_in_executor
在单独的线程中运行阻塞函数。如果您只是发送 http 请求(而不执行任何 cpu 阻塞任务),那么使用 AsyncHTTPClient
将获得比线程更好的性能。
你好@xyres 谢谢你的回复。我查看了'run_in_executor' 我尝试像这样调用函数:await loop.run_in_executor(None, self.blockingFunction()) 同时将 def post(self) 声明为'async' - 并在 def post( self): loop = asyncio.get_running_loop() - 但是我仍然遇到同样的问题。知道应该改变什么吗? ---(P.S.:我稍后将值写入数据库,所以我认为 run_in_executor 看起来比使用 AsynchHTTPClient 更有希望)
编辑:tornadoweb.org/en/stable/faq.html 我原来的 liveblockingFunction() 有参数,需要像这样调用:await loop.run_in_executor(None, self.blockingFunction, param1, param2) 似乎有了这个改变工作。谢谢@xyres
该方法,xyres 说,可能更好。run_in_executor
是执行任何 cpu 阻塞任务时更好的方法(你说,只是可能请求,而不是 cpu 阻塞)。它使用线程池或里面的进程池,会占用更多的资源。
【参考方案1】:
class AddHandler(tornado.web.RequestHandler):
def initialize(self, folderpaths):
self.folderpaths = folderpaths
def blockingFunction(self):
time.sleep(320)
post("AWAKE")
async def post(self):
user = self.get_argument('user')
folderpath = self.get_argument('inpath')
outpath = self.get_argument('outpath')
workflow_value = self.get_argument('workflow')
status_code, status_text = validateInFolder(folderpath)
if (status_code == 200):
logging.info("Status Code 200")
result = self.folderpaths.add_file(user, folderpath, outpath, workflow_value)
self.write(result)
self.finish()
#At this point the path is validated.
#POST response should be send out. Internal process should continue, new
#requests should not be blocked
await loop.run_in_executor(None, self.blockingFunction)
#if this had multiple parameters it would be
#await loop.run_in_executor(None, self.blockingFunction, param1, param2)
谢谢@xyres
进一步阅读:https://www.tornadoweb.org/en/stable/faq.html
【讨论】:
以上是关于Tornado 6.1 非阻塞请求的主要内容,如果未能解决你的问题,请参考以下文章