深入理解yield(三):yield与基于Tornado的异步回调
转自:http://beginman.cn/python/2015/04/06/yield-via-Tornado/
- 作者:BeginMan
- 版权声明:本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
- 发表于 2015-04-06
在深入理解yield(二):yield与协程 和深入理解yield(一):yield原理已经对yield原理及在python中的运用了解了很多,那么接下来就要结合Tornado,进行python异步的分析。
一.异步的实现
异步的实现离不开回调函数,接下来介绍回调函数的概念以及在python中的使用。
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。同步调用是三者当中最简单的,而回调又常常是异步调用的基础,
可以参阅同步调用、回调和异步调用区别加深理解。

图片来源:https://www.ibm.com/developerworks/cn/linux/l-callback/
有时候对回调这个概念比较模糊,在知乎上回调函数(callback)是什么?,举了一个很好的例子:
你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。
下面举一个Python的回调例子
# coding=utf-8
__author__ = 'fang'
def call_back(value):
print 'call back value:', value
def caller(func, arg):
print 'caller'
func(arg)
caller(call_back, 'hello,world')
Tornado异步
tornado提供了一套异步机制,asynchronous装饰器能够使其异步,tornado默认在get()或者post()返回后自动结束HTTP请求(默认在函数处理返回时关闭客户端的连接),当装饰器给定,在函数返回时response不会结束,self.finish()去结束HTTP请求,它的主要工作就是将 RequestHandler 的 _auto_finish 属性置为 false。
如下例子:
#同步阻塞版本
def MainHandler(tornado.web.RequestHandler):
def get(self):
client = tornado.httpclient.HttpClient()
# 阻塞
response = client.fetch("http://www.google.com/")
self.write('Hello World')
这个例子就不在啰嗦了,整体性能就在于访问google的时间.下面展示异步非阻塞的例子:
def MainHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
def get(self):
client = tornado.httpclient.AsyncHTTPClient()
def callback(response):
self.write("Hello World")
self.finish()
client.fetch("http://www.google.com/", callback)
fetch的时候提供callback函数,这样当fetch http请求完成的时候才会去调用callback,而不会阻塞。callback调用完成之后通过finish结束与client的连接。
这种异步回调的缺点就是:拆分代码逻辑,多重回调的繁琐,能不能有一套方案像正常执行逻辑一样使异步能够顺序化去执行呢?在上面的两节yield的学习中可知:因为yield很方便的提供了一套函数挂起,运行的机制,所以我们能够通过yield来将原本是异步的流程变成同步的。,在tornado中具体表现为tornado.gen.
def MainHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
@tornado.gen.engine
def get(self):
client = tornado.httpclient.AsyncHTTPClient()
response = yield tornado.gen.Task(client.fetch, "http://www.google.com/")
self.write("Hello World")
self.finish()
使用gen.engine的decorator,该函数主要就是用来管理generator的流程控制。 使用了gen.Task,在gen.Task内部,会生成一个callback函数,传给async fetch,并执行fetch,因为fetch是一个异步操作,所以会很快返回。 在gen.Task返回之后使用yield,挂起 当fetch的callback执行之后,唤醒挂起的流程继续执行.
那么接下来分析gen源码:
def engine(func):
"""Decorator for asynchronous generators.
异步generators装饰器
任何从这个module生产的生成器必须被这个装饰器所装饰。这个装饰器只用于已经是异步的函数
如:
@tornado.web.asynchronous
@tornado.gen.engine
def get(RequestHandler): #http method.
pass
源码分析:http://blog.xiaogaozi.org/2012/09/21/understanding-tornado-dot-gen/
Any generator that yields objects from this module must be wrapped in this decorator. The decorator only works on functions that are already asynchronous. For `~tornado.web.RequestHandler```get``/``post``/etc methods, this means that both the `tornado.web.asynchronous` and `tornado.gen.engine` decorators must be used (for proper exception handling, ``asynchronous` should come before ``gen.engine``). In most other cases, it means that it doesn't make sense to use ``gen.engine`` on functions that
don't already take a callback argument.
"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
runner = None
def handle_exception(typ, value, tb):
# if the function throws an exception before its first "yield"
# (or is not a generator at all), the Runner won't exist yet.
# However, in that case we haven't reached anything asynchronous
# yet, so we can just let the exception propagate.
if runner is not None:
return runner.handle_exception(typ, value, tb)
return False
with ExceptionStackContext(handle_exception) as deactivate:
# 代表被装饰的http method(如get), 因为在之前所装饰的method 包含yield关键字,所以gen = func()是generator
gen = func(*args, **kwargs)
# 检查是否是generator对象
if isinstance(gen, types.GeneratorType):
# 虽然调用了包含yield的http method,但函数并没有立即执行,只是赋值给了gen
# 可想而知Runner()是来启动生成器函数的,包含next(),send(),throw(),close()等方法
runner = Runner(gen, deactivate)
runner.run()
return
assert gen is None, gen
deactivate()
# no yield, so we're done
return wrapper
了解了gen,接下来我们自己实现一个:
# coding=utf-8
__author__ = 'fang'
import tornado.ioloop
from tornado.httpclient import AsyncHTTPClient
import functools
def task(fun, url):
return functools.partial(fun, url)
def callback(gen, response):
try:
print 'callback:', response
gen.send(response)
except StopIteration:
pass
def sync(func):
def wrapper():
gen = func()
f = gen.next()
print 'aa', f, gen
f(functools.partial(callback, gen))
return wrapper
@sync
def fetch():
response = yield task(AsyncHTTPClient().fetch, 'http://www.suhu.com')
print '1'
print response
print '2'
fetch()
print 3
tornado.ioloop.IOLoop.instance().start()
输出:
aa <functools.partial object at 0x10a992fc8> <generator object fetch at 0x10a6e6460>
3
callback: HTTPResponse(code=200,request_time=0.9294881820678711,buffer=<_io.BytesIO object at 0x10a9b9110>......)
1
HTTPResponse(code=200,request_time=0.9294881820678711,buffer=<_io.BytesIO object at 0x10a9b9110>......)
2
参考
深入理解yield(三):yield与基于Tornado的异步回调的更多相关文章
- Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就绪,挂起,运行) ,***协程概念,yield模拟并发(有缺陷),Greenlet模块(手动切换),Gevent(协程并发)
Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就 ...
- 深入理解Tornado——一个异步web服务器
本人的第一次翻译,转载请注明出处:http://www.cnblogs.com/yiwenshengmei/archive/2011/06/08/understanding_tornado.html原 ...
- (并发编程)进程池线程池--提交任务2种方式+(异步回调)、协程--yield关键字 greenlet ,gevent模块
一:进程池与线程池(同步,异步+回调函数)先造个池子,然后放任务为什么要用“池”:池子使用来限制并发的任务数目,限制我们的计算机在一个自己可承受的范围内去并发地执行任务池子内什么时候装进程:并发的任务 ...
- 基于tornado的文件上传demo
这里,web框架是tornado的4.0版本,文件上传组件,是用的bootstrap-fileinput. 这个小demo,是给合作伙伴提供的,模拟APP上摄像头拍照,上传给后台服务进行图像识别用,识 ...
- 基于tornado的爬虫并发问题
tornado中的coroutine是python中真正意义上的协程,与python3中的asyncio几乎是完全一样的,而且两者之间的future是可以相互转换的,tornado中有与asyncio ...
- python yield、yield from与协程
从生成器到协程 协程是指一个过程,这个过程与调用方协作,产出由调用方提供的值.生成器的调用方可以使用 .send(...)方法发送数据,发送的数据会成为yield表达式的值.因此,生成器可以作为协程使 ...
- python协程--yield和yield from
字典为动词“to yield”给出了两个释义:产出和让步.对于 Python 生成器中的 yield 来说,这两个含义都成立.yield item 这行代码会产出一个值,提供给 next(...) 的 ...
- node 异步回调解决方法之yield
先看如何使用 使用的npm包为genny,npm 安装genny,使用 node -harmony 文件(-harmony 为使用es6属性启动参数) 启动项目 var genny= require( ...
- 深入理解OOP(三):多态和继承(动态绑定和运行时多态)
在前面的文章中,我们介绍了编译期多态.params关键字.实例化.base关键字等.本节我们来关注另外一种多态:运行时多态, 运行时多态也叫迟绑定. 深入理解OOP(一):多态和继承(初期绑定和编译时 ...
随机推荐
- jQuery的noConflict以及插件扩展
一.noConflict函数 JavaScript有很多插件,如果jQuery对象的$与其他插件冲突,我们可以使用noConflict()方法去掉$或者使用其他的符号代替 注:noConflict() ...
- SHA1
整理于互动百科 安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital ...
- jquery deferred promise
<script type="text/javascript">/* Deferredstate (then,done, fail, always,pipe, progr ...
- ARM_Instruction_Set_Encoding_hacking(ARM指令集编码格式解读)
ARM指令集编码格式解读 说明: 1.本文参考的书籍<ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition>中的Cha ...
- liunx用户管理的基本命令
1.passwd 修改用户密码 2.useradd 用户组名 增加用户组 3.su 用户名 切换用户名 4.usermod 用户更改 5.userdel 用户删除
- CodeForces - 1093F:Vasya and Array (DP&计数)
题意:N,K,L,以及给定长度为N的序列,表示其对应的颜色,-1表示还没有涂色,现在让你去涂色,使得最后没有大于等于L的连续的同色的情况. 思路:我们用dp[i][j]表示第i个位置颜色为j的合法方案 ...
- CTF-练习平台-Misc之 想蹭网先接开密码
十三.想蹭网先接开密码 用wireshark打开,WiFi认证过程重点在WPA的四次握手包,找到EAPOL握手协议,另存为 根据题目提示,写字典 #include<stdio.h>int ...
- 关于递归函数中的return位置
1.对于求是否有解的问题一般使用bool dfs() 其中return 可以放在递归式后面 2.对于需要更新解的问题一般使用int dfs() 其中return 不能放在递归式后面,必须放在函数最 ...
- getParameter、getParameterValues、getParameterMap用法详解
首先request中的参数parameter是一个map表,如下例 map={username=[xxx],password=[xxx],hobby=[eat,drink]} (1)当调用getPar ...
- Sort An Unsorted Stack
Given a stack of integers, sort it in ascending order using another temporary stack. Examples: Input ...