聊聊Redis的單線程模型
開(kāi)篇
本文主要來(lái)探討一下 redis 的單線程模型,文章前半部分會(huì)先引用某網(wǎng)絡(luò)課程講解的內(nèi)容(圖片+語(yǔ)言描述),后半部分是本人粗略閱讀 redis 源碼后整理出來(lái)的一份偽代碼,用來(lái)驗(yàn)證文中前半部分的內(nèi)容。
本文對(duì)標(biāo)的 redis 版本是 5.x。
正文
redis 涉及的知識(shí)點(diǎn)有很多,展開(kāi)來(lái)講能聊到操作系統(tǒng),因此為了方便理解,文中做了很多抽象描述。
文件事件處理器
redis 內(nèi)部使用了一個(gè)叫 文件事件處理器( file event handler)的東西,這個(gè)文件事件處理器是 單線程 的,所以才有了 redis 是單線程的這一說(shuō)法。
文件事件處理器的結(jié)構(gòu)如下圖:
它包含 4 個(gè)部分:
- 多個(gè) socketIO
- 多路復(fù)用程序
- 文件事件分派器
- 事件處理器(連接應(yīng)答處理器、命令請(qǐng)求處理器、命令回復(fù)處理器)
文件事件處理器 采用 IO 多路復(fù)用機(jī)制 同時(shí)監(jiān)聽(tīng)多個(gè) socket,根據(jù) socket 上的事件來(lái)選擇對(duì)應(yīng)的事件處理器進(jìn)行處理。多個(gè) socket 可能會(huì)并發(fā)產(chǎn)生不同的操作,每個(gè)操作對(duì)應(yīng)不同的文件事件,但是 IO 多路復(fù)用程序 會(huì)監(jiān)聽(tīng)多個(gè) socket,會(huì)將 socket 產(chǎn)生的事件放入 隊(duì)列 中排隊(duì),事件分派器 每次從隊(duì)列中取出一個(gè)事件,把該事件交給對(duì)應(yīng)的 事件處理器 進(jìn)行處理。
來(lái)看 redis 客戶端與服務(wù)端的一次通信過(guò)程:
1. 接收連接請(qǐng)求:
客戶端 socket01 向 redis 的 server socket 請(qǐng)求建立連接,此時(shí) server socket 會(huì)產(chǎn)生一個(gè) AE_READABLE 事件,
IO 多路復(fù)用程序監(jiān)聽(tīng)到 server socket 產(chǎn)生的事件后,將該事件壓入隊(duì)列中。
文件事件分派器從隊(duì)列中獲取該事件,交給連接應(yīng)答處理器。
連接應(yīng)答處理器會(huì)創(chuàng)建一個(gè)能與客戶端通信的 socket01,并將該 socket01 的 AE_READABLE 事件與 命令請(qǐng)求處理器 關(guān)聯(lián)。
2. 讀取請(qǐng)求內(nèi)容:
假設(shè)此時(shí)客戶端發(fā)送了一個(gè) set key value 請(qǐng)求,此時(shí) redis 中的 socket01 會(huì)產(chǎn)生 AE_READABLE 事件
IO 多路復(fù)用程序?qū)⑹录喝腙?duì)列
事件分派器從隊(duì)列中獲取到該事件,由于前面 socket01 的 AE_READABLE 事件已經(jīng)與命令請(qǐng)求處理器關(guān)聯(lián),因此事件分派器將事件交給命令請(qǐng)求處理器來(lái)處理。
命令請(qǐng)求處理器讀取 socket01 的 key value 并在自己內(nèi)存中完成 key value 的設(shè)置。操作完成后,它會(huì)將 socket01 的 AE_WRITABLE 事件與命令回復(fù)處理器關(guān)聯(lián)。
3. 回復(fù)請(qǐng)求:
如果此時(shí)客戶端準(zhǔn)備好接收返回結(jié)果了,那么 redis 中的 socket01 會(huì)產(chǎn)生一個(gè) AE_WRITABLE 事件,同樣壓入隊(duì)列中,
事件分派器找到相關(guān)聯(lián)的命令回復(fù)處理器
由命令回復(fù)處理器對(duì) socket01 輸入本次操作的一個(gè)結(jié)果,比如 ok,之后解除 socket01 的 AE_WRITABLE 事件與命令回復(fù)處理器的關(guān)聯(lián)。
這樣便完成了一次通信。
redis 事件處理偽代碼
// 入口函數(shù) void aeMain(EventLoop eventLoop) { while(true) { // 文件事件處理器 aeProcessEvents(eventLoop); } } void aeProcessEvents() { // 調(diào)用 epoll_wait 函數(shù),等待I/O事件 (IO 多路復(fù)用程序) int numevents = aeApiPoll(timeval); for(int i=0; i< numevents; i++) { // 從隊(duì)列中取出對(duì)應(yīng)的事件 fileEvent = getFromEventQueue(i); // 處理文件事件 (文件事件分派器) processFileEvent(fileEvent); // 處理時(shí)間事件。(忽略) processTimeEvent(); } } void processFileEvent() { if event == '讀事件' { // 讀處理器 processReadFile(); } if event == '寫(xiě)事件' { // 寫(xiě)處理器 processWriteFile(); } }
redis 源碼
篇幅原因這里就不貼 redis 的源代碼,可以用 vscode 等工具打開(kāi) redis 安裝目錄下的 src 目錄,通過(guò)全局搜索 aeMain 找到入口。
總結(jié)
起初當(dāng)看到別人說(shuō) redis 是單線程時(shí),很容易想成 redis 服務(wù)端只開(kāi)啟了一個(gè)線程用于做所有事情,然而實(shí)際上我們所說(shuō)的 redis 單線程 只是針對(duì) redis 網(wǎng)絡(luò)請(qǐng)求模塊,即文中提到的文件事件處理器。
另外,在第一次看到文中第一張圖時(shí),就感覺(jué)像是一個(gè)線程(IO多路復(fù)用程序)負(fù)責(zé)往隊(duì)列寫(xiě)數(shù)據(jù),另一個(gè)線程(文件事件分派器)負(fù)責(zé)從隊(duì)列里面讀數(shù)據(jù),那么 redis 的單線程到底體現(xiàn)在哪里呢?
最后通過(guò)一頓操作,翻到了相關(guān)源碼,粗略一讀才明白所謂的事件分派器、隊(duì)列等等是這樣的。
到此這篇關(guān)于聊聊Redis的單線程模型的文章就介紹到這了,更多相關(guān)Redis單線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot 啟動(dòng)注解過(guò)程分析
這篇文章主要為大家介紹了Spring Boot 啟動(dòng)注解過(guò)程示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06Netty分布式pipeline管道創(chuàng)建方法跟蹤解析
這篇文章主要為大家介紹了Netty分布式pipeline管道創(chuàng)建方法跟蹤解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03Java正則表達(dá)式matcher.group()用法代碼
這篇文章主要給大家介紹了關(guān)于Java正則表達(dá)式matcher.group()用法的相關(guān)資料,最近在做一個(gè)項(xiàng)目,需要使用matcher.group()方法匹配出需要的內(nèi)容,文中給出了詳細(xì)的代碼示例,需要的朋友可以參考下2023-08-08Java控制臺(tái)輸入數(shù)組并逆序輸出的方法實(shí)例
這篇文章主要介紹了Java手動(dòng)輸入數(shù)組并逆向輸出的方法實(shí)例,需要的朋友可以參考下。2017-08-08SpringBoot3.x打包Docker容器的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot3.x打包Docker容器的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11SpringBoot項(xiàng)目中連接Gauss數(shù)據(jù)庫(kù)
本文主要介紹了SpringBoot項(xiàng)目中連接Gauss數(shù)據(jù)庫(kù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06Jenkins如何實(shí)現(xiàn)自動(dòng)打包部署linux
這篇文章主要介紹了Jenkins如何實(shí)現(xiàn)自動(dòng)打包部署linux,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11idea下如何設(shè)置項(xiàng)目啟動(dòng)的JVM運(yùn)行內(nèi)存大小
這篇文章主要介紹了idea下如何設(shè)置項(xiàng)目啟動(dòng)的JVM運(yùn)行內(nèi)存大小問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12