目录
一.问题引入
tornado 是一个很好的异步非阻塞Python框架,有着优秀的大并发处理能力,使用中出现如下错误:
raise RuntimeError("Cannot write() after finish()")
二.代码
以下是涉及的代码:
thread_pool = ThreadPoolExecutor(2)
def go():
all = selected_customer_all()
return all
class R_SelectionCustomer(MyBackStargeRequestHandler):
@authenticated # 需要身份认证才能访问的处理器
#让一个请求一直保持 连接状态,而不是马上返回,直到一个主处理行为返回。
@gen.coroutine
def get(self):
print("我R_SelectionCustomer被访问了")
#self.get_argument()
all = yield thread_pool.submit(go)
self.write(str(RJ(data=all,total=len(all))))
三.解决
查阅资料:
当一个处理请求的行为被执行之后,这个请求会自动地结束。因为 Tornado 当中使用了 一种非阻塞式的 I/O 模型,所以你可以改变这种默认的处理行为——让一个请求一直保持 连接状态,而不是马上返回,直到一个主处理行为返回。要实现这种处理方式,只需要 使用 tornado.web.asynchronous
装饰器就可以了。
使用了这个装饰器之后,你必须调用 self.finish()
已完成 HTTTP 请求,否则 用户的浏览器会一直处于等待服务器响应的状态。
所有需要在
@gen.coroutine上面加上@tornado.web.asynchronous,并且最后调用 self.finish()表示已完成 HTTTP 请求.
最终代码:
thread_pool = ThreadPoolExecutor(2)
def go():
all = selected_customer_all()
return all
class R_SelectionCustomer(MyBackStargeRequestHandler):
@authenticated # 需要身份认证才能访问的处理器
@tornado.web.asynchronous
@gen.coroutine
def get(self):
print("我R_SelectionCustomer被访问了")
#self.get_argument()
all = yield thread_pool.submit(go)
self.write(str(RJ(data=all,total=len(all))))
# 采用两种装饰器的组合,finish会自动被调用
#self.finish()
四.后续
发现两个装饰器组合使用不需要再调用self.finish()!
五.拓展:
tornado.web.asynchronous
:作用是保持长连接,也就是除非你主动调用self.finish()
方法,否则requestHandler
将不会返回。
tornado.gen.coroutine
:使用协程的方式实现类似异步的处理效果。最新版的tornado,其实不一定需要写tornado.web.asynchronous
装饰器
联系:@asynchronous
会监听@gen.coroutine
的返回结果(Future
),并在@gen.coroutine
装饰的代码段执行完成后自动调用finish
。所以,就有两种方式来调用:
# -----------调用方式1---------------------
# 采用两种装饰器的组合,finish会自动被调用
@web.asynchronous
@gen.coroutine
def get(self):
# ...some code...
self.write('...')
# -----------调用方式2---------------------
@tornado.web.asynchronous
def get(self):
self.my_func()
@tornado.gen.coroutine
def my_func(self):
# ...some code...
self.write('...')
self.finish() # 此处需要手动调用finish