gevent是基于协程的Python网络库。特色:css
基于libev的快速事件循环(Linux上epoll,FreeBSD上kqueue)。html
基于greenlet的轻量级执行单元。python
API的概念和Python标准库一致(如事件,队列)。react
能够配合socket,ssl模块使用。linux
可以使用标准库和第三方模块建立标准的阻塞套接字(gevent.monkey)。git
默认经过线程池进行DNS查询,也可经过c-are(经过GEVENT_RESOLVER=ares环境变量开启)。程序员
TCP/UDP/HTTP服务器github
子进程支持(经过gevent.subprocess)shell
线程池服务器
gevent目前支持python2.5-2.7,python2.6之前的版本若是要使用ssl还须要安装ssl模块。
# pip install gevent
下面的示例展现了如何同时运行任务。
>>> import gevent>>> from gevent import socket>>> urls = ['www.google.com', 'www.example.com', 'www.python.org']>>> jobs = [gevent.spawn(socket.gethostbyname, url) for url in urls]>>> gevent.joinall(jobs, timeout=2)>>> [job.value for job in jobs]['74.125.128.106', '93.184.216.119', '82.94.164.162']
job发起以后,gevent.joinall()等待完成,不超过2秒。结果收集在gevent.Greenlet.value属性。 gevent.socket.gethostbyname()和socket.gethostbyname()的接口同样,但它并不阻塞解释器,其余 greenlet继续畅通无阻的处理请求。
上面例子使gevent.socket进行socket操做。若是使用标准socket模块将有3倍耗时,由于DNS请求是串行的。在greenlet中使用标准socket模块毫无心义,这些模块和包是怎么创建在socket之上的?
monkey patching这时起做用了,gevent.monkey当心地使用兼容副本替换标准socket模块的函数和类。这样,即便是不知道gevent的模块也受益于greenlet环境运行。
>>> from gevent import monkey; monkey.patch_socket()>>> import urllib2 # it's usable from multiple greenlets now
下面是使用urllib2进行下载的实例:
#!/usr/bin/env python# -*- coding: utf-8 -*-# Copyright (c) 2009 Denis Bilenko. See LICENSE for details."""Spawn multiple workers and wait for them to complete"""urls = ['http://www.google.com', 'http://www.yandex.ru', 'http://www.python.org']import geventfrom gevent import monkey# patches stdlib (including socket and ssl modules) to cooperate with other greenletsmonkey.patch_all()import urllib2def 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.wait(jobs)
执行结果:
# ./test.py Starting http://www.google.com Starting http://www.yandex.ru Starting http://www.python.org http://www.google.com: 11246 bytes: '<!doctype html><html itemscope="" itemtype="http:/'http://www.python.org: 20471 bytes: '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Trans'http://www.yandex.ru: 208804 bytes: '<!DOCTYPE html><html class="i-ua_js_no i-ua_css_st'
不像其余网络库,gevent和eventlet相似, 在一个greenlet中隐式开始事件循环。没有必须调用run()或dispatch()的反应器(reactor),在twisted中是有 reactor的。当gevent的API函数想阻塞时,它得到Hub实例(执行时间循环的greenlet),并切换过去。若是没有集线器实例则会动态 建立。
libev提供的事件循环默认使用系统最快轮询机制,设置LIBEV_FLAGS环境变量可指定轮询机制。LIBEV_FLAGS=1为select, LIBEV_FLAGS = 2为poll, LIBEV_FLAGS = 4为epoll,LIBEV_FLAGS = 8为kqueue。请阅读libev文档了解更多信息http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#FUNCTIONS_CONTROLLING_EVENT_LOOPS。
Libev的API位于gevent.core下。注意libev API的回调在Hub的greenlet运行,所以使用同步greenlet的API。可使用spawn()和Event.set()等异步API。
全部greenlets都在同一个操做系统线程调度执行。直到特定的greenlet放弃控制,(调用阻塞函数切换到Hub),其余greenlet才有 机会运行。对于I / O密集型应用程序这一般不是问题,但作CPU密集型或者调用封锁绕过libev事件循环的I/0功能的时会有问题。
通常不须要在greenlet之间同步访问共享对象, 因此Lock和Semaphore类尽管存在,可是不多使用。从线程和多处理等其余概念仍然经常使用,以下:
Event:唤醒在调用Event.wait()方法的greenlets。
AsyncResult:和Event相似,但容许传递值或异常。队列和JoinableQueue。
Queu和JoinableQueue.
greenlet经过建立greenlet实例并调用其start方法发起。(spawn()函数就是作这个的快捷方式)。 start方法给greenlet安排一个开关,当前greenlet放弃控制触发。若是有多个active的事件,将不肯定的顺序一一执行。
若是在执行过程当中出现错误,将没法离开greenlet的边界。未处理的错误致使打印堆栈跟踪及失败函数和参数:
>>> gevent.spawn(lambda : 1/0)>>> gevent.sleep(1)Traceback (most recent call last): ...ZeroDivisionError: integer division or modulo by zero<Greenlet at 0x7f2ec3a4e490: <function <lambda...>> failed with ZeroDivisionError
traceback在greenlet退出时同步打印至sys.stderr。
Greenlet实例有以下有用的方法:
join – waits until the greenlet exits;
kill – interrupts greenlet’s execution;
get – returns the value returned by greenlet or re-raised the exception that killed it.
继承Greenlet类重载其str能够自定义traceback后的字符串。另外还须要重载_run()方法以及在init中调用Greenlet.init(self)。
class MyNoopGreenlet(Greenlet): def __init__(self, seconds): Greenlet.__init__(self) self.seconds = seconds def _run(self): gevent.sleep(self.seconds) def __str__(self): return 'MyNoopGreenlet(%s)' % self.seconds
能够异步结束Greenlet,浙江将恢复等待的greenlet,不继续执行而是引起GreenletExit。
>>> g = MyNoopGreenlet(4)>>> g.start()>>> g.kill()>>> g.deadTrue
GreenletExit及其子类的处理方式不一样于其余异常。GreenletExit不被视为异常状态,不打印traceback。get能够得到GreenletExit是GET,就好像它是由greenlet返回,不是raise。
kill方法能够自定义的异常:
>>> g = MyNoopGreenlet.spawn(5) # spawn() creates a Greenlet and starts it>>> g.kill(Exception("A time to kill"))Traceback (most recent call last): ...Exception: A time to kill MyNoopGreenlet(5) failed with Exception
kill还能够接受timeout参数指定greenlet退的等待秒数。注意,kill不能保证目标greenlet不会忽视该异常,所以给kill传递timeout是个好方法。
gevent的API中的许多函数是同步的,阻塞当前greenlet直到操做完成。例如,kill会等到greenlet结束。多数能够传递参数block=False异步执行。
此外,许多同步函数接受超时参数,指定能够阻塞多久(好比:Event.wait(), Greenlet.join(), Greenlet.kill(), AsyncResult?.get()等)。
socket和SSLObject实例也能够超时,由setTimeout方法设置。
若是这些还不够用,Timeout类能够给任意(yielding)代码块增长超时。
限制并发可使用Pool类(参见实例: dns_mass_resolve.py)
gevent自带的TCP/SSL/HTTP/WSGI服务器。参见实现服务器部分http://www.gevent.org/servers.html。
gevent For the Working Python Developer: 是一个更全面的教程。http://sdiehl.github.io/gevent-tutorial/. 中文版本参见http://xlambda.com/gevent-tutorial/
gevent主页: http://www.gevent.org/
gevent英文文档: http://www.gevent.org/intro.html
gevent程序员指南: http://xlambda.com/gevent-tutorial/
类型:翻译