Tornado 报错:module ‘tornado.web’ has no attribute ‘asynchronous’

发布于 2022-07-08  208 次阅读


问题产生

今天在写一个 Tornado 异步请求的时候,用到了 Tornado 的 tornado.web.asynchronous 装饰器。

import tornado.httpclient
import tornado.web

class CUserHandler(tornado.web.RequestHandler):

    @tornado.web.asynchronous  # 报错
    def post(self):
        client = tornado.httpclient.AsyncHTTPClient()
        future = client.fetch('/api/user')
        future.add_done_callback(self.on_response)

    def on_response(self, future)
        response = future.result()
        self.write(response.body)

结果直接给我报错了,报错信息:AttributeError: module 'tornado.web' has no attribute 'asynchronous'。说是 Tornado.web 没有属性 asynchronous

但是之前做的另一个项目都跑通了,后来仔细查阅了官方文档后发现,竟然是被废弃了。文档片段如下:

原来从 3.1 版本开始,这个异步请求就推荐使用协程 @tornado.gen.coroutine 替代了,到 5.1 版本被弃用,6.0 直接把它删除了。新项目版本直接上 Tornado 6.2,所以才会报错说不存在。而另一个项目在技术选型时就考虑到了新版本可能会有很多变动,所以使用的是较为经典的 4.x 版本。

解决办法

方法一:版本回退

退回 5.1 版本或者更早的版本,不太推荐使用这种方法。如果使用了高版本的新特性,那么回退版本后,所有使用新特性的地方都要改。

  1. 卸载当前 tornado 框架:python -m pip uninstall tornado
  2. 安装 5.1 版本的 Tornado 框架:python -m pip install tornado==5.1

方法二:使用协程

推荐这种方法。即便是异步装饰器 @tornado.web.asynchronous 能用的情况下,也应该优先使用协程加生成器 yield 的方法。用法如下:

import tornado.gen
import tornado.httpclient
import tornado.web

class MainHandler(tornado.web.RequestHandler):

    @tornado.gen.coroutine
    def post(self):
        client = tornado.httpclient.AsyncHTTPClient()
        response = yield client.fetch('/api/user')
        self.write(response.body)

并且这样写还有另一个好处,生成器 yield 将直接返回一个 HTTPResponse 对象。而之前的那种写法只会返回一个处在 pending 状态的 Future 对象,需要调用回调函数,接收转到 finish 状态的 Future 对象。此时,在回调函数中调用 Future 对象的 result 方法才会返回 HTTPResponse 对象。另外,再提醒一下大家,tornado.httpclient.AsyncHTTPClientfetch 方法在新版本中不支持直接传 callback 参数了,需要使用 pending 状态下的 Future 对象的 add_done_callback 方法添加回调函数。

总结

使用 Tornado 框架以来,给我最大的感受就是相对于其它 Python 框架来说,它的性能极为强悍,完全吊打主流的 Django 框架。但是缺点也很明显,国内用 Tornado 的公司少,网上教程极度匮乏,并且 3.x 的版本居多,基本都已经过时了,而官方文档从 5.0 开始只有英文文档。对于我这种英文不好的同学,需要费好多精力去百度翻译。同时官方文档还并不详细。

最开始学习 Tornado,是因为我们公司内部服务端主流语言是 Python,而 Tornado 框架也是我们公司使用最多的一个框架。对 Tornado 的开发运用也让我认识到了它的强大,在 Python 语言自身速度慢的巨大缺陷下,依旧完美地解决了 C10K 问题,能够从容处理上万用户同时在线的场景。