Gen.coroutine原理
在之前一篇博客中讲到要使用tornado的异步特性,必须使用异步的库。否则单个进程阻塞,根本不会达到异步的效果。 Tornado的异步库中最常用的就是自带的AsyncHTTPClient。
在3.0版本之后,gen.coroutine模块显得比较突出。coroutine装饰器可以让本来靠回调的异步编程看起来像同步编程。其中便是利用了Python中生成器的Send函数。在生成器中,yield关键字往往会与正常函数中的return相比。它可以被当成迭代器,从而使用next()返回yield的结果。但是生成器还有另外一个用法,就是使用send方法。在生成器内部可以将yield的结果赋值给一个变量,而这个值是通过外部的生成器client来send的。举一个例子:
def test_yield():
pirnt "test yeild"
says = (yield)
print says
if __name__ == "__main__":
client = test_yield()
client.next()
client.send("hello world")
输出结果如下:
test yeild
hello world
已经在运行的函数会挂起,直到调用它的client使用send方法,原来函数继续运行。而这里的gen.coroutine方法就是异步执行需要的操作,然后等待结果返回之后,再send到原函数,原函数则会继续执行,这样就以同步方式写的代码达到了异步执行的效果。
Tornado异步编程
@gen.coroutine
def post(self):
client = AsyncHTTPClient()
resp = yield client.fetch("https://api.github.com/users")
if resp == 200:
body = escape.json_decode(resy.body)
else:
body = {"message": "client fetch error"}
logger.error("client fetch error %d, %s" % (resp.code, resp.message))
self.write(escape.json_encode(body))
self.finish()
换成函数之后可以变成这样;
@gen.coroutime
def post(self):
resp = yield GetUser()
self.write(resp)
@gen.coroutine
def GetUser():
client = AsyncHTTPClient()
resp = yield client.fetch("https://api.github.com/users")
if resp.code == 200:
resp = escape.json_decode(resp.body)
else:
resp = {"message": "fetch client error"}
logger.error("client fetch error %d, %s" % (resp.code, resp.message))
raise gen.Return(resp)
这里,当把异步封装在一个函数中的时候,并不是像普通程序那样使用return关键字进行返回,gen模块提供了一个gen.Return的方法。是通过raise方法实现的。这个也是和它是使用生成器方式实现有关的。