Unix网络编程中提到了5种网络模型
Blocking IO 阻塞式IO,一次只能处理一个请求。
Noblocking IO 非阻塞式IO
IO multiplexing IO多路复用(高并发框架使用)
Signal Driven IO 信号驱动的IO
Asynchronous IO 异步IO
如何提升服务器的并发能力呢?就是如何处理多个客户端连接
一些常见的提升并发能力的方式
多线程模型,创建新的线程处理请求
多进程模型,创建新的进程处理请求
线程 / 进程创建开销比较大,可以用线程池方式解决
线程 / 进程比较占用资源,难以同时创建太多
现在服务器基本使用 IO多路复用,实现单进程同时处理多个 socket 请求。
IO多路复用就是操作系统提供的同时监听多个 socket 的机制
为了实现高并发需要一种机制并发处理多个 socket
Linux 常见的是 select / poll / epoll 系统调用
可以使用单线程单进程处理多个 socket
为什么 select 就能实现高并发呢?
select 可以同时处理多个 socket,有一个就绪应用程序代码就可以处理它
# 使用IO复用代码格式
while True:
events = sel.select() # 系统阻塞在select,直到有一个socket就绪
for key,mask in events: # 当socket就绪之后就遍历事件
callback = key.data
callback(key.fileobj, mask)
select / poll / epoll 区别
重点是索引就绪文件的时间复杂度,一般优先使用epoll
Python 封装了操作系统的IO多路复用
Python 的IO多路复用基于操作系统实现( select / poll / epoll )
Python2 select 模块
Python3 selectors 模块
EVENT_READ 表示socket可读, EVENT_WRITE 表示socket可写
示例:
下面是一个简单的echo服务器实现 :异步的TCP回显它能够并发的处理客户端请求,把客户端发的再发回去,这就是TCP回显服务器。
import selectors
import socket
sel = selectors.DefaultSelector()
def accept(sock, mask):
conn, addr = sock.accept() # Should be ready
print('accepted', conn, 'from', addr)
conn.setblocking(False)
sel.register(conn, selectors.EVENT_READ, read) # 注册read回调
def read(conn, mask):
data = conn.recv(1000) # Should be ready
if data:
print('echoing', repr(data), 'to', conn)
conn.send(data) # Hope it won't block
else:
print('closing', conn)
sel.unregister(conn)
conn.close()
sock = socket.socket()
sock.bind(('localhost', 1234))
sock.listen(100)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept) # 注册accept回调
while True: # 事件循环
events = sel.select()
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)