线程IO模型
首先必须记住的是,Redis是个单线程程序。
为什么单线程还这么快?
Redis所有数据都在内存里,所有运算都是内存级别的运算,所以速度比在硬盘内操作更快。但是也正是由于是单线程,所以要小心使用那些时间复杂度O(n)的指令。
单线程如何处理那么多的并发客户端连接?
多路复用。
非阻塞IO
socket方法的读写默认都是阻塞的,在python中可以通过socket.socket().setblocking(False)来设置非阻塞IO。
时间轮询(多路复用)
非阻塞IO有个问题就是,线程读写数据可能只做了一部分就返回了,什么时候继续读数据,什么时候继续写数据,应该有个方法来通知它们。
时间轮询API就是来解决这个问题的,早期的select,后来的poll,现在的epoll和kqueque。
#基于select
import socket
import select
import queue
server = socket.socket()
server.bind((‘localhost‘, 8000))
server.listen()
server.setblocking(0)
msg = {}
inputs = [server]
outputs = []
while True:
readable, writeable, exceptional = select.select(inputs, outputs, inputs)
for r in readable:
if r is server:
conn, addr = r.accept()
conn.setblocking(0)
inputs.append(conn)
msg[conn] = queue.Queue()
else:
try:
data = r.recv(1024)
msg[r].put(data)
outputs.append(r)
except ConnectionResetError:
inputs.remove(r)
continue
for w in writeable:
data = msg[w].get()
w.send(data)
outputs.remove(w)
for e in exceptional:
if e in outputs:
outputs.remove(e)
inputs.remove(e)
del msg[e]
# 自动匹配版select,在linux中自动使用epoll,在windows中只能select,在bsd或mac中自动kqueue
import socket
import selectors
server = socket.socket()
server.bind((‘localhost‘, 8000))
server.listen()
server.setblocking(0)
sel = selectors.DefaultSlector()
def accept(server, mask):
conn, addr = server.accept()
conn.setblocking(0)
sel.register(conn, mask, read)
def read(conn, mask):
data = conn.recv(1024)
if data:
conn.send(data)
else:
sel.unregister(conn)
conn.close()
sel.register(server, selectors.EVENT_READ, accept)
while True:
events = sel.select()
for key, mask in events:
callback = key.data
obj = key.fileobj
callback(obj, mask)
不过这些都不用我们去实现,Redis内部已经替我们实现了这种轮询机制,只要记住多路复用是非阻塞IO,并不是异步操作。
原文地址:https://www.cnblogs.com/ikct2017/p/9503422.html
时间: 2024-10-10 00:05:24