redis
基於reactor
模式開發了自己的網路事件處理器,稱之為檔案事件處理器(file event hanlder
)。檔案事件處理器由socket
、io
多路復用程式、檔案事件分派器(dispather
),事件處理器(handler
)四部分組成。io
多路復用程式會同時監聽多個socket
,當被監聽的socket
準備好執行accept
、read
、write
、close
等操作時,與這些操作相對應的檔案事件就會產生。io
多路復用程式會把所有產生事件的socket
壓入乙個佇列中,然後有序地每次僅乙個socket
的方式傳送給檔案事件分派器,檔案事件分派器接收到socket
之後會根據socket
產生的事件型別呼叫對應的事件處理器進行處理。
檔案事件處理器分為幾種:
事件種類:
ae_writable
:當服務端有資料需要回傳給客戶端時,服務端將命令回覆處理器與socket
的ae_writable
事件關聯起來。
redis
的客戶端與服務端的互動過程:
redis 將所有資料放在記憶體中,記憶體的響應時長大約為 100 納秒,這是 redis 的 qps 過萬的重要基礎。
當我們呼叫 scoket 的讀寫方法,預設它們是阻塞的。
read() 方法要傳遞進去乙個引數 n,表示讀取這麼多位元組後再返回,如果沒有讀夠 n 位元組執行緒就會阻塞,直到新的資料到來或者連線關閉了, read 方法才可以返回,執行緒才能繼續處理。
write() 方法會首先把資料寫到系統核心為 scoket 分配的寫緩衝區中,當寫快取區滿溢,即寫快取區中的資料還沒有寫入到磁碟,就有新的資料要寫道寫快取區時,write() 方法就會阻塞,直到寫快取區中有空閒空間。
非阻塞 io 在 scoket 物件上提供了乙個選項non_blocking
,當這個選項開啟時,讀寫方法不會阻塞,而是能讀多少讀多少,能寫多少寫多少。
能讀多少取決於核心為 scoket 分配的讀緩衝區的大小,能寫多少取決於核心為 scoket 分配的寫緩衝區的剩餘空間大小。讀方法和寫方法都會通過返回值來告知程式實際讀寫了多少位元組資料。
有了非阻塞 io 意味著執行緒在讀寫 io 時可以不必再阻塞了,讀寫可以瞬間完成然後執行緒可以繼續幹別的事了。
檔案描述符:核心(kernel)利用檔案描述符(file descriptor)來訪問檔案。檔案描述符是非負整數。開啟現存盤案或新建檔案時,核心會返回乙個檔案描述符。讀寫檔案也需要使用檔案描述符來指定待讀寫的檔案。多路 i/o 復用模型是利用select、poll、epoll可以同時監察多個流的 i/o 事件的能力,在空閒的時候,會把當前執行緒阻塞掉,當有乙個或多個流有i/o事件時,就從阻塞態中喚醒,於是程式就會輪詢一遍所有的流(epoll是只輪詢那些真正發出了事件的流),並且只依次順序的處理就緒的流,這種做法就避免了大量的無用操作。這裡「多路」指的是多個網路連線,「復用」指的是復用同乙個執行緒。採用多路 i/o 復用技術可以讓單個執行緒高效的處理多個連線請求(儘量減少網路io的時間消耗),且redis在記憶體中運算元據的速度非常快(記憶體內的操作不會成為這裡的效能瓶頸),主要以上兩點造就了redis具有很高的吞吐量。
io
復用只需要阻塞在select
,poll
或者epoll
,可以同時處理和管理多個連線。缺點是當select
、poll
或者epoll
管理的連線數過少時,這種模型將退化成阻塞io
模型。並且還多了一次系統呼叫:一次select
、poll
或者epoll
一次recvfrom
。
select、poll、epoll 區別:
最大連線數
fd劇增後帶來的io效率問題
訊息傳遞方式
select
單個程序所能開啟的最大連線數有fd_setsize巨集定義,其大小是32個整數的大小(在32位的機器上,大小就是32*32,同理64位機器上fd_setsize為32*64
因為每次呼叫時都會對連線進行線性遍歷,所以隨著fd的增加會造成遍歷速度慢的「線性下降效能問題」。
核心需要將訊息傳遞到使用者空間,都需要核心拷貝動作
poll
基於鍊錶來儲存的,沒有最大連線數的限制
同上核心需要將訊息傳遞到使用者空間,都需要核心拷貝動作
epoll
接數有上限,但是很大,1g記憶體的機器上可以開啟10萬左右的連線
因為epoll核心中實現是根據每個fd上的callback函式來實現的,只有活躍的socket才會主動呼叫callback,所以在活躍socket較少的情況下,使用epoll沒有前面兩者的線性下降的效能問題,但是所有socket都很活躍的情況下,可能會有效能問題。
利用mmap()檔案對映記憶體加速與核心空間的訊息傳遞;即epoll使用mmap減少複製開銷
單執行緒能帶來幾個好處:
epoll是linux提供的系統實現,核心方法只有三個epoll_create、epollctl、epollwait。epoll效率高,是因為基於紅黑樹、雙向鍊錶、事件**機制。
epoll_create(int size)引數size並不是限制了epoll所能監聽的檔案描述符最大個數,只是對核心初始分配內部資料結構的乙個建議。在linux 2.6.8後,size 引數被忽略,但是必須傳乙個比 0 大的數。呼叫epoll_create後,會占用乙個fd值。在linux下可以檢視/proc/$$/fd/ 檔案描述符。使用完,需要呼叫close關閉。核心功能:
1.建立乙個epoll檔案描述符
2.建立eventpoll,其中包含紅黑樹cache和雙向鍊錶
int epollctl(int epfd, int op, int fd, struct epollevent *event);op操作型別,用三個巨集epoll_ctl_add,epoll_ctl_del,epoll_ctl_mod,來分別表示增刪改對fd的監聽。核心功能:
1.對指定描述符fd執行op的繫結操作
2.把fd寫入紅黑樹,同時在核心註冊**函式
int epollwait(int epfd, struct epollevent *events, int maxevents, int timeout);引數events是就緒事件,用來得到想要獲得的事件集合。maxevents表示的events有多大,maxevents的值必須大於0,引數timeout是超時時間。epollwait會阻塞,直到乙個檔案描述符觸發了事件,或者被乙個訊號處理函式打斷,或者timeout超時。返回值是需要處理的fd數量。核心功能:
1.獲取epfd上的io事件
參考:
redis的單執行緒模型
redis使用文字事件處理器file event handler 整個檔案事件處理器是單執行緒的,所以才叫做單執行緒模型,他採用io多路復用機制同時監聽多個socket,根據socket上的事件來選擇對應的事件處理器進行處理 檔案事件處理器的結構包含4個部分 多個socket可能會併發產生不同的操作...
Redis單執行緒
redis 的單執行緒主要是指 redis 的網路 io 和鍵值對讀寫是由乙個執行緒來完成的,這也是 redis 對外提供鍵值儲存服務的主要流程。當多個客戶端發起命令,這些命令併發執行時,在redis內部,會排隊逐個執行,也就是執行命令的那個操作是由乙個執行緒執行的。但 redis 的其他功能,比如...
為什麼Redis選擇單執行緒模型?
redis從一開始就使用單執行緒模型處理來自客戶端的絕大多數的網路請求,即redis可以支援io多路復用。除此之外還有其他原因 1 使用單執行緒模型能帶來更好的維護性,方便開發和除錯。2 使用單執行緒模型也能併發的處理客戶端的請求。3 redis服務中執行的絕大多數操作的效能瓶頸都不是cpu。雖然多...