標籤:nal default queue 模型 oca defaults linux 多工 api
線程IO模型
首先必須記住的是,Redis是個單線程程式。
為什麼單線程還這麼快?
Redis所有資料都在記憶體裡,所有運算都是記憶體層級的運算,所以速度比在硬碟內操作更快。但是也正是由於是單線程,所以要小心使用那些時間複雜度O(n)的指令。
單線程如何處理那麼多的並發用戶端串連?
多工。
非阻塞IO
socket方法的讀寫預設都是阻塞的,在python中可以通過socket.socket().setblocking(False)來設定非阻塞IO。
時間輪詢(多工)
非阻塞IO有個問題就是,線程讀寫資料可能只做了一部分就返回了,什麼時候繼續讀資料,什麼時候繼續寫資料,應該有個方法來通知它們。
時間輪詢API就是來解決這個問題的,早期的select,後來的poll,現在的epoll和kqueque。
#基於selectimport socketimport selectimport queueserver = 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中自動kqueueimport socketimport selectorsserver = 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,並不是非同步作業。
深入Redis(十)線程IO模型