龙卷风网络异步



我正在尝试使用这段代码来理解@tornado.web.async。预期的代码应该处理异步 Web 请求,但它似乎没有按预期工作。有两个终点:

1) http://localhost:5000/A  (This is the time consuming request and 
takes a few seconds)
2) http://localhost:5000/B (This is the fast request and takes no time to return.

但是,当我点击浏览器转到 http://localhost:5000/A 然后当它正在运行时转到 http://localhost:5000/B 第二个请求排队并仅在 A 完成后运行。 换句话说,一个任务很耗时,但它会阻止另一个更快的任务。我做错了什么?

import tornado.web
from tornado.ioloop import IOLoop
import sys, random, signal

class TestHandler(tornado.web.RequestHandler):
"""
In below function goes your time consuming task
"""

def background_task(self):
sm = 0
for i in range(10 ** 8):
sm = sm + 1
return str(sm + random.randint(0, sm)) + "n"
@tornado.web.asynchronous
def get(self):
""" Request that asynchronously calls background task. """
res = self.background_task()
self.write(str(res))
self.finish()

class TestHandler2(tornado.web.RequestHandler):
@tornado.web.asynchronous
def get(self):
self.write('Response from server: ' + str(random.randint(0, 100000)) + "n")
self.finish()
def sigterm_handler(signal, frame):
# save the state here or do whatever you want
print('SIGTERM: got kill, exiting')
sys.exit(0)
def main(argv):
signal.signal(signal.SIGTERM, sigterm_handler)
try:
if argv:
print ":argv:", argv
application = tornado.web.Application([
(r"/A", TestHandler),
(r"/B", TestHandler2),
])
application.listen(5000)
IOLoop.instance().start()
except KeyboardInterrupt:
print "Caught interrupt"
except Exception as e:
print e.message
finally:
print "App: exited"
if __name__ == '__main__':
sys.exit(main(sys.argv))

根据文档:

为了最大限度地降低并发连接的成本,Tornado 使用 单线程事件循环。这意味着所有应用程序代码 应该以异步和非阻塞为目标,因为只有一个 操作可以一次处于活动状态。

要实现这一目标,您需要正确准备RequestHandler。如果函数只执行同步操作,那么简单地将装饰器@tornado.web.asynchronous添加到任何函数(getpost等)是不够的。

@tornado.web.asynchronous装饰器是做什么的?

让我们看一下get函数。语句以同步方式一个接一个地执行。一旦工作完成并且函数返回,请求就会关闭。正在引擎盖下呼吁self.finish()。但是,当我们使用@tornado.web.asynchronous装饰器时,请求在函数返回后不会关闭。因此,用户必须调用self.finish()才能完成 HTTP 请求。如果没有这个装饰器,当 get() 方法返回时,请求会自动完成。

查看此页面中的"示例 21" - tornado.web.async:

@web.asynchronous
def get(self):
http = httpclient.AsyncHTTPClient()
http.fetch("http://example.com/", self._on_download)
def _on_download(self, response):
self.finish()

get()函数对 http://example.com/页执行异步调用。假设此调用是一个长期操作。所以http.fetch()函数被调用,过了一会儿get()函数返回(http.fetch()仍在后台运行)。龙卷风的IOLoop可以向前移动,在获取 http://example.com/数据的同时为下一个请求提供服务。http.fetch()函数调用完成后,将调用回调函数 -self._on_download。然后调用self.finish()并最终关闭请求。这是用户可以在浏览器中看到结果的时刻。

由于httpclient.AsyncHTTPClient(),这是可能的.如果使用httpclient.HTTPClient()的同步版本,则需要等待调用 http://example.com/完成。然后get()函数将返回并处理下一个请求。

总而言之,如果您在建议RequestHandler中使用异步代码@tornado.web.asynchronous则可以使用装饰器。否则,它对性能没有太大影响。

编辑:要解决您的问题,您可以在单独的线程中运行耗时的函数。下面是TestHandler类的简单示例:

class TestHandler(tornado.web.RequestHandler):
def on_finish(self, response):
self.write(response)
self.finish()
def async_function(base_function):
@functools.wraps(base_function)
def run_in_a_thread(*args, **kwargs):
func_t = threading.Thread(target=base_function, args=args, kwargs=kwargs)
func_t.start()
return run_in_a_thread
@async_function
def background_task(self, callback):
sm = 0
for i in range(10 ** 8):
sm = sm + 1
callback(str(sm + random.randint(0, sm)))
@tornado.web.asynchronous
def get(self):
res = self.background_task(self.on_finish)

您还需要将这些导入添加到代码中:

import threading
import functools
import threading

async_function是一个装饰器功能。如果您不熟悉该主题,我建议您阅读(例如:装饰器)并自行尝试。通常,我们的装饰器允许函数立即返回(因此主程序执行可以继续),并且处理在单独的线程中同时进行。线程中的函数完成后,我们调用一个回调函数,该函数将结果写出给最终用户并关闭连接。

最新更新