第8节,tornado 编写异步代码

tornado 推荐使用协程来编写异步代码,有两种方式,一种是使用基于生成器的协程,一种是使用async/await 关键字编写的协程。

1. 基于生成器的协程

协程使用 Python 中的关键字 yield 来替代链式回调来实现挂起和继续程序的执行,需要使用gen.coroutine 来装饰生成器函数

import tornado.ioloop
from tornado.web import RequestHandler, Application
from tornado.httpserver import HTTPServer
from tornado.options import options, define
from tornado.httpclient import AsyncHTTPClient
from tornado import gen

define('port', default=8000, help='监听端口')


class HelloHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        url = "http://coolpython.net"
        response_code = yield self.fetch_coroutine(url)
        self.finish(str(response_code))

    @gen.coroutine
    def fetch_coroutine(self, url):
        http_client = AsyncHTTPClient()
        response = yield http_client.fetch(url)
        return response.code

if __name__ == '__main__':
    options.parse_command_line()
    handlers_routes = [
        (r'/', HelloHandler)
    ]
    app = Application(handlers=handlers_routes)
    http_server = HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.current().start()

从处理请求的get方法开始,就需要使用gen.coroutine 装饰器,fetch_coroutine 是一个协程,调用它时,必须加上yield 关键字,这样get方法也是一个基础生成器的协程。

2. 基于async/await 的协程

Python 3.5 引入了 async 和 await 关键字,从 Tornado 4.3 开始 你可以用他们来替换gen.coroutine 和 yield, 替换的方法如下:

  1. 用async def 来替换gen.coroutine
  2. 用await 替换yield

HelloHandler类可以改造成这样

class HelloHandler(RequestHandler):

    async def get(self):
        url = "http://coolpython.net"
        response_code = await self.fetch_coroutine(url)
        self.finish(str(response_code))

    async def fetch_coroutine(self, url):
        http_client = AsyncHTTPClient()
        response = await http_client.fetch(url)
        return response.code

代码编写上更加自然,因此我推荐你使用这种方法来编写tornado 异步代码。

3. 何时使用异步代码

tornado 是一个异步网络库,很多人没有搞清楚它的基本原理就想当然的认为只要使用tornado就能够获得很高的并发处理能力,这是严重的错误。

tornado 既是一个web框架,也可以当做一个wsgi 服务器来使用,它通过提供异步IO能力让你在单进程条件下就能够获得很高的并发处理能力,前提是你能用对它。

引起阻塞的原因如果是CPU计算过于密集,那么此种情况下tornado无能为力,单进程条件下,CPU导致的阻塞是没有办法进行异步处理的。

tornado 能够异步处理的是那些网络IO操作,比如本文例子中的向coolpython.net 发送http请求,再比如请求对数据库进行增删改查。对于网络IO,也需要使用对应的异步库,回看前面的程序,我使用的是tornado自己提供的AsyncHTTPClient,它是一个http异步客户端,如果你使用requests 这个非常流行的http客户端库,那么就做不到异步,因为requests的IO处理是同步的。

对于mysql, redis, mongodb, 你所熟悉的python 客户端库在tornado里都是不能提供异步IO能力的,他们仍然可以使用,但是已经让tornado 所编写的web服务失去了异步处理请求的能力,其性能远不如使用uwsgi或者gunicorn 多进程部署flask编写的web 服务。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案