转自:http://blog.xiaogaozi.org/2012/09/21/understanding-tornado-dot-gen/

理解 tornado.gen

SEP 21ST, 2012

Tornado 通过 @asynchronous decorator 来实现异步请求,但使用的时候必须将 request handler 和 callback 分离开,tornado.gen 模块可以帮助我们在一个函数里完成这两个工作。下面是官方的一个例子:

1
2
3
4
5
6
7
8
class GenAsyncHandler(RequestHandler):
@asynchronous
@gen.engine
def get(self):
http_client = AsyncHTTPClient()
response = yield gen.Task(http_client.fetch, "http://example.com")
do_something_with_response(response)
self.render("template.html")

这里用到了两个 decorator 稍显复杂,第一个 @asynchronous 会首先被执行,它的主要工作就是将 RequestHandler 的 _auto_finish 属性置为 false,如下:

web.pydownload

1
2
3
4
5
6
7
8
9
10
def asynchronous(method):
@functools.wraps(method)
def wrapper(self, *args, **kwargs):
if self.application._wsgi:
raise Exception("@asynchronous is not supported for WSGI apps")
self._auto_finish = False
with stack_context.ExceptionStackContext(
self._stack_context_handle_exception):
return method(self, *args, **kwargs)
return wrapper

接着就是最重要的 @gen.engine,这里充分利用了 generator 的各种特性,首先来看 @gen.engine 的实现(我删减了部分代码以简化理解):

gen.pydownload

1
2
3
4
5
6
7
8
9
def engine(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
gen = func(*args, **kwargs)
if isinstance(gen, types.GeneratorType):
runner = Runner(gen)
runner.run()
return
return wrapper

局部变量 gen 代表第一段代码里的 get 函数,因为 get 包含了 yield 语句,因此成为了一个 generator。注意这里 get 并没有被执行,只是赋给了 gen。接下来是运行 Runner 对象的 run 函数。在理解 run 之前需要知道 generator 是通过调用 next() 或者 send() 来启动,启动之后会在遇到 yield 的地方 hold 住,然后将 yield 后面的语句的返回值返回给调用者,generator 此时即处于暂停运行状态,所有上下文都会保存。再次调用 next() 或 send() 便会恢复 generator 的运行,如果不再遇到 yield 语句就会抛出 StopIteration 异常。在恢复运行的同时 yield 语句本身会有返回值,如果是通过调用 next() 来恢复的,那么返回值永远是 None,而如果是通过 send() 则返回值取决于传给 send() 的参数。更多关于 generator 的说明请参考官方文档

结合第一段的示例代码,可以想到 run 干的工作可能就是启动 generator,然后获得 gen.Task对象并调用 http_client.fetch 函数,等回调回来之后恢复 generator 的运行,最后将回调的返回值通过 send() 赋给 response。下面是我简化后的代码。

gen.pydownload

1
2
3
4
5
6
7
8
9
10
11
12
def run(self):
while True:
if not self.yield_point.is_ready():
return
next = self.yield_point.get_result()
try:
yielded = self.gen.send(next)
except StopIteration:
return
if isinstance(yielded, YieldPoint):
self.yield_point = yielded
self.yield_point.start(self)

第 3 行检查回调是否完成,第一次运行 run 总是会返回 True。第 5 行获取回调的返回值,同样的第一次运行返回的是 None。将 None 传给 send() 启动 generator,yielded 即是 gen.Task 对象,第 12 行调用 start 开始运行我们真正需要运行的函数,对应到示例代码就是http_client.fetch 函数,同时将 Runner 的 result_callback 作为回调函数。如下:

gen.pydownload

1
2
3
4
5
6
7
8
9
10
11
def result_callback(self, key):
def inner(*args, **kwargs):
if kwargs or len(args) > 1:
result = Arguments(args, kwargs)
elif args:
result = args[0]
else:
result = None
self.results[key] = result
self.run()
return inner

在得到回调返回值之后再次调用 run,通过 get_result 获取返回值,最后将返回值返回赋给 response,继续 request handler 的代码流程。

理解 tornado.gen的更多相关文章

  1. tornado.gen 模块解析

    转自:http://strawhatfy.github.io/2015/07/22/Tornado.gen/ 引言 注:正文中引用的 Tornado 代码除特别说明外,都默认引用自 Tornado 4 ...

  2. 深入理解Tornado——一个异步web服务器

    本人的第一次翻译,转载请注明出处:http://www.cnblogs.com/yiwenshengmei/archive/2011/06/08/understanding_tornado.html原 ...

  3. Tornado @tornado.gen.coroutine 与 yield

    在使用 Tornado 的过程中产生了以下疑问: 什么时候需要给函数增加 @tornado.gen.coroutine 什么时候调用函数需要 yield @tornado.gen.coroutine ...

  4. Python之路(四十一):通过项目来深入理解tornado

    Tornado之路   引子 与其感慨路难行,不如马上出发 目录 通过项目来深入理解tornado(一):tornado基础回顾 通过项目来深入理解tornado(二):AsyncHttpClient ...

  5. 如何捕捉@tornado.gen.coroutine里的异常

    from tornado import gen from tornado.ioloop import IOLoop @gen.coroutine def throw(a,b): try: a/b ra ...

  6. 理解tornado

    计算密集型:多进程 IO密集型:多线程   能产生IO阻塞的情况很多,比如网络通讯.磁盘读写.当发生阻塞时,CPU是闲置的,此时如果就一个线程就没法处理其他事情了. 所以对于含有IO阻塞的环境,多线程 ...

  7. tornado.gen.coroutine-协程

    http://blog.csdn.net/seeground/article/details/49488281  

  8. 深入理解yield(三):yield与基于Tornado的异步回调

    转自:http://beginman.cn/python/2015/04/06/yield-via-Tornado/ 作者:BeginMan 版权声明:本文版权归作者所有,欢迎转载,但未经作者同意必须 ...

  9. tornado 杂记

    一.建立一个简单的 hello world 网页 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import tornado.iolo ...

随机推荐

  1. 逆向路由器固件之敏感信息泄露 Part2

    之前的文章中详细介绍了各种解包路由器固件的工具.解包之后就获得了固件中的文件.下一步就是分析文件寻找漏洞了.这次分析的目标是Trendnet路由器,分析的漏洞是一个远程获取路由器权限的漏洞. 初步分析 ...

  2. union-find算法

     1.背景 <算法>一书中提到了关于算法的一些基本思想 优秀的算法因为能够解决实际的问题而变得更为重要: 高效算法的代码可以很简单: 理解某个实现的性能特点是一项有趣而令人满足的挑战: 在 ...

  3. ARM裸板开发:07_IIC 通过IIC总线接口读写时钟芯片时间参数实现的总结

    问题一:程序直接在iRAM内部可正常执行,而程序搬移(Nand ->SDRAM)之后,就不能正常运行了 #define NAND_SECTOR_SIZE 2048 /* 读函数 */ void ...

  4. 类似select下拉选择框同时又支持手动输入的元素 datalist 介绍。

    有时候我们会有这样的需求,通过使用下拉菜单给用户一定的选择范围,同时又可以使用户在找不到选择项的时候手动输入.这个时候我们就需要用到html5的datalist属性了. datalist包含<o ...

  5. 古典、SOA、传统、K8S、ServiceMesh

    古典.SOA.传统.K8S.ServiceMesh 十几年前就有一些公司开始践行服务拆分以及SOA,六年前有了微服务的概念,于是大家开始思考SOA和微服务的关系和区别.最近三年Spring Cloud ...

  6. 自动化工具-jenkins

    jenkins自动化工具使用教程 自动化构建.测试.部署.代码检测越来越重要.主要有一下几点原因 企业做大,项目变多,多端支持(web,h5,小程序等) 微服务提倡高内聚低耦合,项目因拆分变多 Dev ...

  7. Start Developing iOS Apps (Swift) 开始开发iOS应用(Swift)

    http://www.cnblogs.com/tianjian/category/704953.html 构建基础的用户界面 Build a Basic UI http://www.cnblogs.c ...

  8. leetcode:Palindrome Number【Python版】

    一次AC 题目要求中有空间限制,因此没有采用字符串由量变向中间逐个对比的方法,而是采用计算翻转之后的数字与x是否相等的方法: class Solution: # @return a boolean d ...

  9. mysql学习--mysql必知必会

      上图为数据库操作分类:     下面的操作參考(mysql必知必会) 创建数据库 运行脚本建表: mysql> create database mytest; Query OK, 1 row ...

  10. JQuery 在网页中查询

    最近遇到客户的一个需求,要在网页中添加一个Search 功能,其实对于网页的搜索,Ctrl+F,真的是非常足够了,但是客户的需求,不得不做,这里就做了个关于Jquery Search function ...