gevent和tornado异步
原文:http://www.pywave.com/2012/08/17/about-gevent-and-tornado/
还是前几月的时候,几乎在同一时间,自己接触到了 Gevent 和 Tornado 这两个已经不新的东西,那时那个 思绪混乱啊!似乎都支持异步,似乎都是无阻塞(non-blocking),性能似乎都好到个不行 (猛击)。知道两者虽是单线程, 但基于无阻塞的特性,战斗力那个是嗖嗖地上涨,运用得当的话,hold住上K个连接不是问题。虽然很感兴趣,虽然完全没弄清楚两者内里的实质,但为了完成工作,略略了解了基本的应用后,卷起手袖就上啦。 当然,作为一个有志的程序员,在满足了现实的迫切需要后,一颗渴望知其所以然的心便开始蠢蠢欲动。
从 Tornado 说起
刚开始,对 Tornado 的感觉最为新鲜,在官网介绍里其是一个无阻塞的Web服务器以及相关工具的集合,但 个人更为倾向其为一个颇为完备的微型 web 框架。Tornado 性能好的关键是其无阻塞异步的特性,但这魔术 似的效果是如何达成的呢?迷思与困惑。我那小脑袋里的思维还停留于多进程(多线程)那样的并发模型中, 实在有点难以理解 Tornado 的异步机制。
通过查阅各式文章以及源代码,整体的框架脉络开始逐渐在脑海中显现出来。其实,Tornado 的异步模型 是由事件驱动以及特定的回调函数(callback)所组成的!一直没有弄明白,Tornado 具体是如何实现 无阻塞异步,当清楚了事件驱动和回调函数的概念后,事情似乎又变得简单起来了。
对于一般的程序,在执行阶段若遇到 I/O 事件,整个进程将被阻塞住,直到 I/O 事件结束,程序又继续执行。 接设我们对一些 I/O 事件进行了定制,使其可以立即返回(即无阻塞),那么程序将能立即继续执行。但 问题又来了,那当 I/O 事件完成后又该怎么办呢?此时,回调函数的威力就出来了,我只需要将进行特定 处理的回调函数与该 I/O 事件绑定起来,当该 I/O 事件完成后就调用绑定的回调函数,就可以处理具体的 I/O 事件啦。啊,似乎还有一个问题,回调函数要如何与 I/O 事件绑定起来?最简单的想法是,直接通过 一个 while True 循环不断的轮询,当检测到 I/O 事件完成了即触发回调函数。但是,这样的效率当然不会 高,利用系统中高效的 I/O 事件轮询机制(epoll on Linux, kqueue on most BSD)就是最明智的 解决方案。于是,无阻塞 I/O +事件驱动+高效轮询方式便组成了 Tornado 的异步模型。
Tornado 的核心是 ioloop 和 iostream 这两个模块,前者提供了一个高效的 I/O 事件循环,后者则封装了 一个无阻塞的 socket 。通过向 ioloop 中添加网络 I/O 事件,利用无阻塞的 socket ,再搭配相应的回调 函数,便可达到梦寐以求的高效异步执行啦。多说无益,来看一下具体的示例:

from tornado import ioloop
from tornado.httpclient import AsyncHTTPClient urls = ['http://www.google.com', 'http://www.yandex.ru', 'http://www.python.org'] def print_head(response):
print ('%s: %s bytes: %r' % (response.request.url,
len(response.body),
response.body[:50])) http_client = AsyncHTTPClient()
for url in urls:
print ('Starting %s' % url)
http_client.fetch(url, print_head)
ioloop.IOLoop.instance().start()

因为使用了 AsyncHTTPClient 来处理请求操作,整个示例是异步执行的,即三个url请求无等待的依次发出。 我们可以看到 fetch 方法使用了 print_head 函数来作为回调函数,这意味着,当 fetch 完成了请求操作, 相应的 print_head 函数便会被触发调用。恩,... 额,...,乍看起来,使用 Tornado 进行异步编程似乎 并不难,让人跃跃欲试。但实际上,在现实生活中,事件驱动的编程还是会很费脑力,需要一定的创造性思维。 不过,这也许是 Tornado 受欢迎的原因之一呢。 :)
再来看下 Gevent
Gevent 是基于协程(coroutine)实现的 Python 网络库,使用了轻量级的 greenlet 作为执行单元,并 基于 libevent 事件循环构建了直观的调用接口。
当时看到这样的描述,脑袋的第一反应是,协程??稍稍了解后,发现协程其实也不是什么高深的概念,协程 也被称为微线程,一看这别名就知道跟线程应该很类似。作为类比倒也可以这么认为,两者关键的区别在于, 线程是由系统进行调度的,而协程是由用户自己进行调度的。当知道这一事实后,立刻想到,这自行调度灵活 肯定是会很灵活,但要调度的话可是很有难度的吧?调度的方法暂时不谈,除了更为灵活外,自行调度的直接 结果当然就是省去了系统调度(什么用户态转内核态,以及什么 context switch),因此协程间切换的资源 消耗很小,再配合协程生成成本很低的另一特点,这可真是相当的美妙。事实上,Python 语言本身就支持基础 的协程的概念,generator 是其中的产物(这里)。
对于 Gevent,其使用的协程实际上就是 greenlet 。当你使用 greenlet 生成了一些协程,就可以在这些 协程里不断跳转执行,两个 greenlet 之间的跳转被称为切换(switch)。通过切换,我们就可以实现对协程 的调度。还应该知道的是,每个 greenlet 都拥有一个父 greenlet ,这是在 greenlet 初始化时就确定的。 当一个 greenlet 执行完毕后,执行权会切换到其父 greenlet 中。实际上,所有的 greenlet 会被组织成 一颗树,树根便是最“老资格”的 greenlet ,这个老 greenlet 确定了各 greenlet 间的逻辑关系。
上面说到协程必须自行调度,不会是要自己构造一个调度器吧?这当然可以做到,但不是必须,因为 Gevent 已经基于 greenlet 和 libevent 封装了许多基础常用的库,例如 socket 、event 和 queue 等,只要使用 这些库进行开发,或者对使用的标准库或第三方库打一下补丁(monket patch),就能保证生成的各协程在 I/O 等待时正确地进行切换,从而实现无阻塞的异步执行。
刚接触 Gevent 时,感觉跟传统的并发编程很类似,但了解渐深后,才发现这货实际上跟 Tornado 更为类似。 因为, Gevent 本质上也是事件驱动。实现的策略可以是,在将要执行 I/O 阻塞事件时,先在事件循环中对该事件 进行注册,关联的回调函数便是对当前协程的切换操作(current_greenlet.switch()),注册成功后即 切换回当前协程的父协程中进行执行(current_greenlet.parent.switch())。当注册的 I/O 事件被 触发后,事件循环在恰当时机便会执行该回调函数,也就是切换到原先的协程继续执行程序。从而,就实现 无阻塞的 I/O 事件处理。怎样,是否感觉相当的有趣? :)
Gevent 了不得的地方还在于,我们能像编写一般程序那样来编写异步程序,这可是弥足珍贵。为了更直观的 显示,让我们来看一下具体的运行示例:

import gevent
from gevent import monkey
# patches stdlib (including socket and ssl modules) to cooperate with other greenlets
monkey.patch_all() import urllib2 urls = ['http://www.google.com', 'http://www.yandex.ru', 'http://www.python.org'] def print_head(url):
print ('Starting %s' % url)
data = urllib2.urlopen(url).read()
print ('%s: %s bytes: %r' % (url, len(data), data[:50])) jobs = [gevent.spawn(print_head, url) for url in urls] gevent.joinall(jobs)

上面示例做的事情实际上跟前面 Tornado 的示例是一样,同样是异步的对url进行请求。在我看来,使用 Gevent 进行编程,无论是可读性还是可操作性都能让人满意。但也要清楚,在实际操作中,为了达到较理想 效果,经常还是需要根据不同的情况对代码进行一些相应的“雕琢”。还有一点很常被人忽略, Gevent 是 基于协程实现的 Python 网络库,其适用面更多的是在于网络 I/O 频繁的需求里,很多情况下 Gevent 可能 并不是很好的选择。总的来说,Gevent 确实很讨人喜爱,性能好,开销小,代码易维护,是广大 pythoner 手中的一大利器。
总要总结一下
作为一名 Python 程序员,在探究和使用 Tornado 与 Gevent 的过程里,除了得到许多思考的乐趣外,最 让人高兴的是收获了一些全新的视野。使用 Python 编程的好处之一便是,可以很容易地跳出语言的框框去看 各式问题,从而提高自己对于程序设计的总体认识。人生苦短,我用Python! :-)
gevent和tornado异步的更多相关文章
- 浅谈 Gevent 与 Tornado(转)
原文:http://www.pywave.com/2012/08/17/about-gevent-and-tornado/ 还是前几月的时候,几乎在同一时间,自己接触到了 Gevent 和 Torna ...
- 5.(基础)tornado异步
终于到了传说中的异步了,感觉异步这个名字听起来就很酷酷的,以前还不是多擅长Python时,就跑去看twisted的源码,结果给我幼小的心灵留下了创伤.反正包括我在内,都知道异步编程很强大,但是却很少在 ...
- tornado异步请求的理解(转)
tornado异步请求的理解 http://www.kankanews.com/ICkengine/archives/88953.shtml 官网第一段话: Tornado is a Python w ...
- 使用Tornado异步接入第三方(支付宝)支付
目前国内比较流行的第三方支付主要有支付宝和微信支付,博主最近研究了下如何用Python接入支付宝支付,这里我以Tornado作为web框架,接入支付宝构造支付接口. 使用Tornado异步接入支付宝支 ...
- Tornado异步非阻塞的使用以及原理
Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快.得利于其 非阻塞的方式和对 epoll 的运用,Tornado ...
- Python Web框架 tornado 异步原理
Python Web框架 tornado 异步原理 参考:http://www.jb51.net/article/64747.htm 待整理
- Tornado异步(2)
Tornado异步 因为epoll主要是用来解决网络IO的并发问题,所以Tornado的异步编程也主要体现在网络IO的异步上,即异步Web请求. 1. tornado.httpclient.Async ...
- tornado异步(1)
1. 同步 我们用两个函数来模拟两个客户端请求,并依次进行处理: # coding:utf-8 def req_a(): """模拟请求a""&quo ...
- tornado异步请求响应速度的实例测试
tornado异步请求响应速度的实例测试
随机推荐
- Yii 语言设置 中文提示信息
1. 在main.php配置文件中加入 'language'=>'zh_cn', 注: 在URL中追加参数lang=zh_cn即可实现中文 2. 在Controller方法中添加 publi ...
- protected internal == internal
总结:在同程序集下,protected internal类型修饰的成员变量可以在基类或派生类的类内.类外访问(同程序集下protected internal和internal访问性质相同,此处保留了i ...
- android--------Android内存分析工具的使用
内存分析(in-memory analytics)是我们编写速度快.效率高的代码必不可少的知识.如果自己编写的代码在内存的分配一无所知,我想这样的程序让你去优化,应该是无从下手的.那么内存分析是什么? ...
- HDU-1226 超级密码 (BFS+剪枝)
Problem Description Ignatius花了一个星期的时间终于找到了传说中的宝藏,宝藏被放在一个房间里,房间的门用密码锁起来了,在门旁边的墙上有一些关于密码的提示信息:密 码是一个C进 ...
- 快速SQL调优/优化(SQL TUNING)——1分钟搞定超慢SQL
前几天,一个用户的研发人员找到我了,说他们有个SQL语句非常慢,我说多慢?他们说:半个小时也没出结果.于是问他们要了SQL语句和执行计划,SQL语句就不能再这里贴出来了,下面是调整前的执行计划(略去某 ...
- POJ 2109 巧妙解法
Int最大是10^9.所以一般思路是二分+高精度.但是double 范围是10^(-307)-10^308所以可以用double型.k^n=p.所以有k=p^(1/n). 见代码: #include& ...
- module_param和module_param_array用法
如何向模块传递参数? Linux kernel 提供了一个简单的框架.利用module_param和module_param_arra来实现. 1. module_param(name, type, ...
- bzoj2209
题解: splay打标机 往下传递 记录x,y为化简后,区间有多少(,) 代码: #include<bits/stdc++.h> ; using namespace std; ],sum[ ...
- Python Django 之 MVT
一.Django的MVT模式 M: Model, 模型 与MVC中的M相同,负责对数据的处理 V: View, 视图 与MVC中的C类似,负责处理用户请求,调用M和T,响应请求 T: Template ...
- 元素大小-偏移量(offset)客户区大小(client)滚动大小(scroll)
一.偏移量---offset 1.定位父级 在理解偏移大小之前,首先要理解offsetParent.人们并没有把offsetParent翻译为偏移父级,而是翻译成定位父级,很大原因是offsetPar ...