常见的异步io模块asyncio、gevent、twisted、tornado
核心技术为select()和协程
异步io请求的本质则是【非阻塞Socket】+【IO多路复用】
协程在这里不是一个必须使用的技术,在使用select()事件驱动循环本身就可以达到单线程异步的效果
io协程在遇到阻塞时进行切换,其实现需要依赖select()事件循环进行切换
协程本质是一种上下文切换技术,通过生成器yield记录状态的特性来实现
r,w,e = select.select([rlist],[wlist],[error],timeout)
select:采用事件轮询方式,即在while True下不断轮询所有的socket,
某个socket有数据返回时通知用户进程,最大轮询事件为1024,可以跨平台,水平触发
poll:方式相同,没有最大轮询事件数量限制
epoll:方式不同,比如100个连接,有两个活跃了,epoll会告诉用户这两个两个活跃了,直接取就ok了,而select是循环一遍。
select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。
如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO
的web server性能更好,可能延迟还更大。
1 import socket,select 2 3 socket_list = [ 4 ('www.baidu.com',80), 5 ('202.89.233.100',80), 6 ('101.37.225.65',80), 7 ] 8 soc_list = [] 9 conn_list=[] 10 for s in socket_list: 11 c = socket.socket() 12 c.setblocking(False) 13 try: 14 c.connect(s) 15 except Exception as e: 16 pass 17 soc_list.append(c) 18 conn_list.append(c) 19 print(soc_list) 20 while True: 21 r,w,error = select.select(soc_list,conn_list,[]) 22 for i in soc_list: 23 try: 24 while True: 25 data = i.sock.recv(8096) 26 print(i) 27 if not data: 28 soc_list.remove(i) 29 except Exception as e: 30 pass 31 32 for i in conn_list: 33 i.sendall("""GET /index HTTP/1.0 Host: www.baidu.com """) 34 conn_list.remove(i)
生产者消费者的例子,yield from的简单用法
yield from用来向生成器(协程)传送数据 ,等价于for i in generator(): yield i
v= yield from ,v为generator()的返回值
1 def consumer_work(): 2 i='' 3 while True: 4 n = yield 5 if n is None: 6 break 7 print('吃了包子%s' % n) 8 return 'end' 9 10 def consumer(con): 11 while True: 12 v = yield from con 13 print(v) 14 15 def producer(c): 16 next(c) 17 for i in range(10): 18 print('生产了包子%s' % i) 19 c.send(i) 20 c.send(None) 21 22 23 con = consumer_work() 24 c = consumer(con) 25 producer(c)