废话不多说,直接上代码

__auth__ = "aleimu"__doc__ = "学习tornado6.0+ 版本与python3.7+"import timeimport asyncioimport tornado.genimport tornado.webimport tornado.ioloopimport tornado.httpserver  # tornado的HTTP服务器实现from tornado.options import define, optionsfrom tornado.httpclient import HTTPClient, AsyncHTTPClientfrom requests import getsettings = {'debug': True}url = "http://127.0.0.1:5000/"  # 这是另个服务,请求5s后返回结果# RuntimeError: Cannot run the event loop while another loop is running# 解释:HTTPClient内部写 loop.run_xxx,因为那是启动event loop的命令,通常只再最最最外面用一次,之后的代码都应假设 loop 已经在运转了。def synchronous_fetch(url):    print("synchronous_fetch")    try:        http_client = HTTPClient()        time.sleep(5)        response = http_client.fetch(url)        print(response.body)    except Exception as e:        print("Error: " + str(e))        return str(e)    http_client.close()    return response.body# 替代synchronous_fetch的同步请求,没有内置loop.run_xxxdef synchronous_get(url):    response = get(url)    time.sleep(5)    print("synchronous_fetch")    return response.text# 简单的模拟异步操作,这里之后应该替换成各种异步库的函数async def sleep():    print("start sleep")    await asyncio.sleep(5)    print("end sleep")# 异步请求async def asynchronous_fetch(url):    http_client = AsyncHTTPClient()    response = await http_client.fetch(url)    print("asynchronous_fetch")    return response.body# 测试class MainHandler(tornado.web.RequestHandler):    def get(self):        self.write("Hello, world:%s" % self.request.request_time())        self.finish()        print("not finish!")        return# 同步阻塞class synchronous_fetcher(tornado.web.RequestHandler):    def get(self):        self.write("%s,%s" % (synchronous_fetch(url), self.request.request_time()))# 同步阻塞class synchronous_geter(tornado.web.RequestHandler):    def get(self):        self.write("%s,%s" % (synchronous_get(url), self.request.request_time()))# 异步阻塞,我以为curl "127.0.0.1:8888/1" 总耗时希望为5s,可是是25s,看来异步没搞好,以下的函数都是基于此改进的class asynchronous_fetcher_1(tornado.web.RequestHandler):    async def get(self):        body = await asynchronous_fetch(url)        for i in range(3):            print("skip %s" % i)            await tornado.gen.sleep(5)        time.sleep(5)        print("end request")        self.write("%s,%s" % (body, self.request.request_time()))# curl "127.0.0.1:8888/1"# b'{\n  "data": "123"\n}\n',25.026000022888184# 异步阻塞,效果同上,这里只是证明 tornado.gen.sleep(5)和asyncio.sleep(5) 效果一致class asynchronous_fetcher_2(tornado.web.RequestHandler):    async def get(self):        body = await asynchronous_fetch(url)  # 关注协程完成后返回的结果        for i in range(3):            print("skip %s" % i)            await sleep()        time.sleep(5)        print("end request")        self.write("%s,%s" % (body, self.request.request_time()))# curl "127.0.0.1:8888/2"# b'{\n  "data": "123"\n}\n',25.039999961853027# 异步非阻塞-将部分异步操作放入组中,实现loop管理class asynchronous_fetcher_3(tornado.web.RequestHandler):    async def get(self):        body = await asynchronous_fetch(url)        await asyncio.wait([sleep() for i in range(3)])        print("end request")        self.write("%s,%s" % (body, self.request.request_time()))# curl "127.0.0.1:8888/3"# b'{\n  "data": "123"\n}\n',10.001000165939331# 异步非阻塞-将所有异步操作放入组中,实现loop管理class asynchronous_fetcher_4(tornado.web.RequestHandler):    async def get(self):        task_list = [sleep() for i in range(3)]        task_list.append(asynchronous_fetch(url))        body = await asyncio.wait(task_list)  # 将所有异步操作的结果返回,但是是无序的,要是需要返回结果的话解析起来比较麻烦        print("end request:", body)        # print(type(body), len(body),type(body[0]),len(body[0]),type(body[0]))        self.write("%s,%s" % ([x.result() for x in body[0] if x.result() is not None][0],                              self.request.request_time()))# curl "127.0.0.1:8888/4"# b'{\n  "data": "123"\n}\n',5.006999969482422def make_app():    return tornado.web.Application([        (r"/", MainHandler),        (r"/1", asynchronous_fetcher_1),        (r"/2", asynchronous_fetcher_2),        (r"/3", asynchronous_fetcher_3),        (r"/4", asynchronous_fetcher_4),        (r"/5", synchronous_fetcher),        (r"/6", synchronous_geter),    ], **settings)if __name__ == "__main__":    print("server start!")    app = make_app()    server = tornado.httpserver.HTTPServer(app)    server.bind(8888)    server.start(1)  # forks one process per cpu,windows上无法fork,这里默认为1    tornado.ioloop.IOLoop.current().start()

总结

1.Tornado使用单线程事件循环,写的不好,会阻塞的非常严重,比如synchronous_geter2.flask+celery可以完成常见的异步任务3.await语法只能出现在通过async修饰的函数中4.可以看到tornado.gen.coroutine, tornado.concurrent.run_on_executor,tornado.web.asynchronous,tornado.gen.coroutine等这些装饰器都不在经常使用了,都由async和await代替

参考文档:

https://zhuanlan.zhihu.com/p/27258289   # Python async/await入门指南http://www.tornadoweb.org/en/stable/guide/intro.html    # 这个官网https://www.osgeo.cn/tornado/guide/intro.html   #Tornado 1.0 - Tornado 6.0的更新说明,以及6.0版本的中文文档,适合英语不好的人阅读https://www.osgeo.cn/tornado/releases/v5.0.0.html#  在Python 3上, IOLoop 总是包装asyncio事件循环。

On Python 3, IOLoop is always a wrapper around the asyncio event loop.
这是我重新复习tornado的原因,tornado放弃了之前自己实现的tornado.ioloop,全面拥抱asyncio的event_loop.这个改动是非常大的,
而且阅读tornado的源码可以发现其中大部分函数都支持了类型检验,和返回值提示,值得阅读.