看了几天tornado的源码,终于理解了异步IO的基本模型。
简单来说就是在一个大循环中,处理事件监听和事件处理,关键点是所有IO操作都设置成非阻塞的模式,然后由事件循环监听。
用epoll来实现一个简易版的EventLoop。
如下:
from select import epoll
class EventLoop():
def __init__(self):
self.handlers = {}
#生成一个epoll对象
self._poll = epoll()
def add_handlers(self, fd, callback, events):
"""
添加回调及注册监听事件
"""
self.handlers[fd] = callback
self._poll.register(fd, events)
def remove_handler(self, fd):
"""
移除回调及移除监听事件
"""
del self.handlers[fd]
self._poll.unregister(fd)
def start(self):
while True:
#事件主循环
events = self._poll.poll(10)
for event in events:
#如果有事件或者超时,则调用注册的回调函数
self.handlers[event[0]](event[0], event[1])
print(events)
io_loop = EventLoop()
简易的非阻塞多用户tcp服务器
def read_ready(conn, fd, event):
"""
读消息回调函数
"""
while True:
try:
text = conn.recv(1024)
except Exception as e:
if e.args[0] not in (errno.EWOULDBLOCK, errno.EAGAIN):
raise
return
if (text == b''):
#消息结束,关闭连接并移除事件
conn.close()
io_loop.remove_handler(fd)
break
print(text)
def connect_ready(sock, fd, event):
while True:
try:
conn, address = sock.accept()
print(conn, address)
except Exception as e:
if e.args[0] not in (errno.EWOULDBLOCK, errno.EAGAIN):
raise
return
#建立链接,设置连接非阻塞模式
conn.setblocking(0)
#生成绑定连接的偏函数
callback = partial(read_ready, conn)
#注册事件
io_loop.add_handlers(conn.fileno(), callback, EPOLLIN)
sock = socket(family=AF_INET)
sock.bind(("", 8888))
sock.listen()
#设置socket非阻塞模式
sock.setblocking(0)
#生成绑定sock的偏函数
callback = partial(connect_ready, sock)
#注册事件
io_loop.add_handlers(sock.fileno(), callback, EPOLLIN)
#启动事件循环
io_loop.start()