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异步请求响应速度的实例测试
随机推荐
- Jersey 2.x 基于 Servlet 的服务器端应用
下面的依赖通常应用到应用服务器上(servlet 容器),同时这个应用服务器上没有整合任何 JAX-RS 的实现. 因此,这个应用服务器需要包含有 JAX-RS API 和 Jersey 实现,同时部 ...
- 安卓出现Invalid layout of java.lang.String at value
Project->Properties->Run/Debug setting->选择类->classpath->删除Bootstrap Entries下面的文件 参考
- P1758 [NOI2009]管道取珠
考虑这个式子的意义. 不妨看做进行了两轮操作,这个式子显然等价于两次操作后得到的序列相同的方案数. 这个东西显然是可以dp的. 随便优化一下就成了O(n^3)
- python-day21--序列化模块模块
什么叫序列化——将原本的字典.列表等内容转换成一个字符串的过程就叫做序列化 序列化的目的: 1.以某种存储形式使自定义对象持久化: 2.将对象从一个地方传递到另一个地方. 3.使程序更具维护性. ...
- Eureka服务注册过程详解之IpAddress(详解eureka.instance.prefer-ip-address = true 与 eureka.instance.prefer-ip-address)
分析,eureka.instance.prefer-ip-address 本节解释为什么配置eureka.instance.prefer-ip-address = true时,注册到Eureka Se ...
- log4j配置文件位置详解
自动加载配置文件: (1)如果采用log4j输出日志,要对log4j加载配置文件的过程有所了解.log4j启动时,默认会寻找source folder下的log4j.xml配置文件,若没有,会寻找lo ...
- C#窗体如何通过keybd_event()函数模拟键盘按键(组合键)产生事件
如何模拟键盘按键触发产生的事件,比如模拟按下Alt + F4 关闭当前程序,Ctrl+Shift 切换输入法等 可以通过win32api 键盘事件 keybd_event() 来实现 1.定义键盘按键 ...
- 通过ReRes让chrome拥有路径映射的autoResponse功能。
前端开发过程中,经常会有需要对远程环境调试的需求.比如,修改线上bug,开发环境不在本地等等.我们需要把远程css文件或者js映射到本地的文件上,通过修改本地文件进行调试和开发.通常我们可以通过以下方 ...
- 通过url获取参数信息
运行结果如下: <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <ti ...
- bzoj1601
题解: 简单生成树 代码: #include<bits/stdc++.h> using namespace std; ; int n,dis[N],f[N],a[N][N],ans; in ...