聊聊Redis的單線程模型

開篇

本文主要來探討一下 redis 的單線程模型,文章前半部分會先引用某網絡課程講解的內容(圖片+語言描述),後半部分是本人粗略閱讀 redis 源碼後整理出來的一份偽代碼,用來驗證文中前半部分的內容。

本文對標的 redis 版本是 5.x。

正文

redis 涉及的知識點有很多,展開來講能聊到操作系統,因此為瞭方便理解,文中做瞭很多抽象描述。

文件事件處理器

redis 內部使用瞭一個叫 文件事件處理器( file event handler)的東西,這個文件事件處理器是 單線程 的,所以才有瞭 redis 是單線程的這一說法。

文件事件處理器的結構如下圖:

它包含 4 個部分:

  • 多個 socketIO
  • 多路復用程序
  • 文件事件分派器
  • 事件處理器(連接應答處理器、命令請求處理器、命令回復處理器)

文件事件處理器 采用 IO 多路復用機制 同時監聽多個 socket,根據 socket 上的事件來選擇對應的事件處理器進行處理。多個 socket 可能會並發產生不同的操作,每個操作對應不同的文件事件,但是 IO 多路復用程序 會監聽多個 socket,會將 socket 產生的事件放入 隊列 中排隊,事件分派器 每次從隊列中取出一個事件,把該事件交給對應的 事件處理器 進行處理。

來看 redis 客戶端與服務端的一次通信過程:

1. 接收連接請求:

客戶端 socket01 向 redis 的 server socket 請求建立連接,此時 server socket 會產生一個 AE_READABLE 事件,

IO 多路復用程序監聽到 server socket 產生的事件後,將該事件壓入隊列中。

文件事件分派器從隊列中獲取該事件,交給連接應答處理器。

連接應答處理器會創建一個能與客戶端通信的 socket01,並將該 socket01 的 AE_READABLE 事件與 命令請求處理器 關聯。

2. 讀取請求內容:

假設此時客戶端發送瞭一個 set key value 請求,此時 redis 中的 socket01 會產生 AE_READABLE 事件

IO 多路復用程序將事件壓入隊列

事件分派器從隊列中獲取到該事件,由於前面 socket01 的 AE_READABLE 事件已經與命令請求處理器關聯,因此事件分派器將事件交給命令請求處理器來處理。

命令請求處理器讀取 socket01 的 key value 並在自己內存中完成 key value 的設置。操作完成後,它會將 socket01 的 AE_WRITABLE 事件與命令回復處理器關聯。

3. 回復請求:

如果此時客戶端準備好接收返回結果瞭,那麼 redis 中的 socket01 會產生一個 AE_WRITABLE 事件,同樣壓入隊列中,

事件分派器找到相關聯的命令回復處理器

由命令回復處理器對 socket01 輸入本次操作的一個結果,比如 ok,之後解除 socket01 的 AE_WRITABLE 事件與命令回復處理器的關聯。

這樣便完成瞭一次通信。

redis 事件處理偽代碼

// 入口函數
void aeMain(EventLoop eventLoop) {
    while(true) {
    	// 文件事件處理器
    	aeProcessEvents(eventLoop);  	
    }
}
void aeProcessEvents() {
	// 調用 epoll_wait 函數,等待I/O事件 (IO 多路復用程序)
    int numevents = aeApiPoll(timeval);
    for(int i=0; i< numevents; i++) {
    	// 從隊列中取出對應的事件
    	fileEvent = getFromEventQueue(i);
    	// 處理文件事件	(文件事件分派器)
    	processFileEvent(fileEvent);
    	// 處理時間事件。(忽略)
    	processTimeEvent();
    }
}
void processFileEvent() {
	if event == '讀事件' {
		// 讀處理器
		processReadFile();
	} 
	if event == '寫事件' {
		// 寫處理器
		processWriteFile();
	}
}

redis 源碼

篇幅原因這裡就不貼 redis 的源代碼,可以用 vscode 等工具打開 redis 安裝目錄下的 src 目錄,通過全局搜索 aeMain 找到入口。

總結

起初當看到別人說 redis 是單線程時,很容易想成 redis 服務端隻開啟瞭一個線程用於做所有事情,然而實際上我們所說的 redis 單線程 隻是針對 redis 網絡請求模塊,即文中提到的文件事件處理器。

另外,在第一次看到文中第一張圖時,就感覺像是一個線程(IO多路復用程序)負責往隊列寫數據,另一個線程(文件事件分派器)負責從隊列裡面讀數據,那麼 redis 的單線程到底體現在哪裡呢?

最後通過一頓操作,翻到瞭相關源碼,粗略一讀才明白所謂的事件分派器、隊列等等是這樣的。

到此這篇關於聊聊Redis的單線程模型的文章就介紹到這瞭,更多相關Redis單線程內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: