python庫之selectors 基於epoll的TP傳輸層實現和 Windows之IOCP
在之前的部落格中已經總結過分別在windows和linux作業系統下實現socket高併發(I/O非同步)的方法,可以參考基於epoll的TP傳輸層實現和Windows之IOCP
下面對Python中實現socket高併發的selectors庫進行總結,官方參考文件:https://docs.python.org/3/library/selectors.html
1. 示例程式碼
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) 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) while True: events = sel.select() for key, mask inevents: callback = key.data callback(key.fileobj, mask)
上面示例程式碼來自官方文件,接下來對關鍵程式碼進行重點說明
2. 重點知識說明
(1)conn.setblocking(False)
設定socket的阻塞或非阻塞模式
阻塞模式下當試圖對該檔案描述符進行讀寫時,如果當時沒有東西可讀,或者暫時不可寫,程式就進入等待狀態,直到有東西可讀或者可寫為止
非阻塞模式下如果沒有東西可讀,或者不可寫,讀寫函式馬上返回,而不會等待
(2)sel.register(conn, selectors.EVENT_READ, read)
對描述符進行註冊,也就是對該描述符的EVENT_READ事件進行監聽,當又READ事件通知時,呼叫回撥函式read
selectors庫提供了兩個監聽事件:EVENT_READ和EVENT_WRITE
(3)sel.unregister(conn)
登出描述符
(4)events = sel.select()
函式原型為:abstractmethod select
(timeout=None)
該函式是實現I/O非同步的關鍵,等待,直到一些已註冊的檔案物件準備就緒,或者超時。
如果timeout>0,則指定最大等待時間,以秒為單位,如果超時沒有,則呼叫將阻塞,直到被監視的檔案物件準備就緒。如果timeout< 0,呼叫將不會阻塞,並將報告當前就緒的檔案物件。
該函式返回一個元組(key, events)
key為class selectors.
SelectorKey
物件,SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data'])
fileobj為註冊的檔案物件
fd為檔案描述符
data為與檔案物件相關聯的自定義資料,如上面的回撥函式
明確上面的4個知識點後會覺得selectors庫的使用很簡單
最後對DefaultSelector進行說明,DefaultSelector會根據當前作業系統型別自己選擇selector型別
if 'KqueueSelector' in globals(): DefaultSelector = KqueueSelector elif 'EpollSelector' in globals(): DefaultSelector = EpollSelector elif 'DevpollSelector' in globals(): DefaultSelector = DevpollSelector elif 'PollSelector' in globals(): DefaultSelector = PollSelector else: DefaultSelector = SelectSelector