本篇将讨论gevent的两架马车-libev和greenlet如何协同工作的。
gevent事件驱动底层使用了libev,我们先看看如何单独使用gevent中的事件循环。
#coding=utf8
import socket
import gevent
from gevent.core import loop
def f():
s, address = sock.accept()
print address
s.send("hello world\r\n")
loop = loop()
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.bind(("localhost",8000))
sock.listen(10)
io = loop.io(sock.fileno(),1) #1代表read
io.start(f)
loop.run()
代码很简单,使用core.loop新建了一个loop实例,通过io加入socket读事件,通过start设置回调,然后run启动事件循环,一个简单的helloworld服务器搭建好了,可以通过telnet localhost 8000看响应结果。gevent的整个事件循环是在hub.run中启动的,
def run(self):
assert self is getcurrent(), 'Do not call Hub.run() directly'
while True:
loop = self.loop
loop.error_handler = self
try:
loop.run()
finally:
loop.error_handler = None # break the refcount cycle
self.parent.throw(LoopExit('This operation would block forever'))
上面的self.loop和我们上面自己新建的loop对象是一样的,下面我们通过socket的recv函数看时间是如何注册到loop中。gevent的socket对象被gevent重新封装,原始socket就是下面的self._sock
我们来看看gevent的socket一次recv做了什么操作。
gevent/socket.py
def recv(self, *args):
sock = self._sock # keeping the reference so that fd is not closed during waiting
while True:
try:
return sock.recv(*args) # 1.如果此时socket已经有数据,则直接return
except error:
#没有数据将会抛出异常,且errno为EWOULDBLOCK
ex = sys.exc_info()[1]
if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
raise
# QQQ without clearing exc_info test__refcount.test_clean_exit fails
sys.exc_clear()
#此时将该文件描述符的”读事件“加入到loop中
self._wait(self._read_event)
"""self._wait会调用hub.wait,
def wait(self, watcher):
waiter = Waiter()
unique = object()
watcher.start(waiter.switch, unique) #这个watcher就是上面说的loop.io()实例,waiter.switch就是回调函数
try:
result = waiter.get()
assert result is unique, 'Invalid switch into %s: %r (expected %r)' % (getcurrent(), result, unique)
finally:
watcher.stop()
当loop捕获到”可读事件“时,将会回调waiter.switch方法,此时将回到这里(因为while循环)继续执行sock.recv(*args)
一般来说当重新recv时肯定是可以读到数据的,将直接返回
"""
上面的self._read_event = io(fileno, 1),再次回到while大循环中,将直接return sock.recv的结果。我们知道socke.recv(1024)可能返回的并没有1024字节,这要看此时缓冲区已接受多少字节,所以说数据可能一次没有读完,所以可能会触发多次
EWOULDBLOCK,多次读取,只有recv为空字符串时才代表读取结束。典型的读取整个数据一般如下所示:
buff = []
while 1:
s = socket.recv(1024)
if n