redis的多路複用是什麼鬼

有沒有人和我一樣, 自打知道了redis, 就一直聽說什麼redis單線程, 使用了多路複用等等. 天真的我以為多路複用是redis實現的技術. 今天才發現, 我被自己騙了, 多路複用是系統來實現的. 對不起自己的專業了.

為了引出多路複用, 我來大膽設想一下技術的發展路程.

前提

一個應用程序, 想對外提供服務, 一般都是通過建立套接字監聽端口來實現, 也就是socket. 在這個時候, 應用對外提供服務的過程大概是這樣.

  1. 創建套接字
  2. 綁定端口號
  3. 開始監聽
  4. 當監聽到連接時, 調用系統read去讀取內容, 但是讀取操作是阻塞的(也就是說,如果主線程處理read,就不能接收其他連接了, 所以只能開新的線程去處理這個事情)

畫個醜醜的流程圖:

redis的多路複用是什麼鬼

問題分析

這個流程的問題很明顯, 會不停的創建線程, 當然, 可以維護一個線程池. 但是線程之間的不停切換也是消耗資源的. 而且也不可能無限的創建線程. 那我如果想一個線程處理呢? 從上面流程圖能看的出來, 問題出在阻塞上面. 如果read操作可以立刻返回結果, 如果沒有讀到數據, 就可以繼續處理後邊的事情了.

簡版

整個簡單版本. 主線程維護一個所有連接的列表, 每次循環讀取所有列表, 有數據就處理, 沒有就跳過.

  1. 創建套接字
  2. 綁定端口號
  3. 開始監聽
  4. 監聽到連接, 將連接加到連接列表中, 循環讀取連接列表中的所有連接, 對有數據的進行處理

畫個醜圖:

redis的多路複用是什麼鬼

問題分析

現在這樣處理貌似是比開線程要好一些了, 但是事實是這樣麼? 眾所周知, 其中的read操作是調用系統函數, 簡單說就是要進行進程的切換, 從用戶進程切換到系統進程. 連接少還好, 如果有十萬個連接, 甚至更多呢? 每次循環都會頻繁的調用系統函數, 可能十萬次調用, 甚至其中有數據的只有一次, 其餘調用都白掉了. 這無疑降低了性能.

其實解決方法說起來也很容易想到. 問題出在系統調用上, 頻繁的系統調用導致了問題的出現. 如果能夠一次性將需要查詢的所有數據都發給系統, 讓系統進行查詢, 那不就只需要一次切換就可以了麼?.

select版本

為了解決上面的問題. 可以批量將待查詢的連接發給系統. 出現了select, 這就是說的 多路複用 了. 在系統 中, 無論是監聽端口還是建立了連接, 程序拿到的都是一個文件描述符, 將這些文件描述符批量查詢就是了.

直接上醜圖:

redis的多路複用是什麼鬼

這裡和上一個版本相比, 將循環檢查交到了系統去做, 只發生一次進程的切換. select 簡單說, 就是你告訴系統, 你需要哪些數據, 你同幫你遍歷找找, 然後將結果返回給你.

問題分析

這樣確實要好上一些, 但是上面的問題還沒有完全解決. 比如有10萬個連接, 其中有數據的只有一個, 那就回有9999次無效的操作, 雖然這些無效的操作是由系統做的, 但不一樣麼, 系統做的無效操作, 你應用程序也得等著啊. 而且每次查詢都要把所有的需要的都傳過去, 10萬個就要傳10萬了, 藍瘦.

問題出在哪呢? 需要循環遍歷, 是因為不知道哪些連接是有數據的, 所以只能一個一個的看. 如果可以不 需要遍歷, 直接知道哪些連接是有數據的, 然後直接拿到數據返回就好了.

epoll

跟上一個版本相比, 現在不通過批量查詢的方式了, 而是通過回調的方式. 簡單說, 建立一個需要回調的連接, 將需要監聽的文件描述符都扔給他, 當有新數據到達時, 會返回給你.

上醜圖:

redis的多路複用是什麼鬼

看著是不是有點暈了? 其實它和select版本的區別簡單來說, epoll是將你需要監聽的列表交給系統維護, 這樣當有新數據來的時候, 系統知道這是你要的, 等你下次來拿的時候, 直接給你了, 少去了上面的系統遍歷. 同時, 也沒有select查詢時那一大堆參數, 每次都只調用一次進行綁定即可.

那系統是怎麼知道新數據的到來呢? 這裡靠的是事件中斷, 忘得差不多了, 回頭再看看.

epoll 簡單說就是, 你告訴系統, 你需要哪些數據, 然後等著, 有數據了系統就通知你, 然後你去讀.


以上select, epoll是兩種多路複用的技術, 當然, 還有多路複用還有其他的.

據說redis的多路複用對系統方法進行了封裝, 不過我還沒看, 再議!!!


分享到:


相關文章: