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/
類型:翻譯