淺談Redis的事件驅(qū)動(dòng)模型
Redis 作為一個(gè) Client-Server 架構(gòu)的數(shù)據(jù)庫(kù),其源碼中少不了用來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)通信的部分。而你應(yīng)該也清楚,通常系統(tǒng)實(shí)現(xiàn)網(wǎng)絡(luò)通信的基本方法是使用Socket編程模型,,包括創(chuàng)建 Socket、監(jiān)聽端口、處理連接請(qǐng)求和讀寫請(qǐng)求。但是,由于基本的 Socket 編程模型一次只能處理一個(gè)客戶端連接上的請(qǐng)求,所以當(dāng)要處理高并發(fā)請(qǐng)求時(shí),一種方案就是使用多線程,讓每個(gè)線程負(fù)責(zé)處理一個(gè)客戶端的請(qǐng)求。
而 Redis 負(fù)責(zé)客戶端請(qǐng)求解析和處理的線程只有一個(gè),那么如果直接采用基本 Socket 模型,就會(huì)影響 Redis 支持高并發(fā)的客戶端訪問(wèn)。
因此,為了實(shí)現(xiàn)高并發(fā)的網(wǎng)絡(luò)通信,我們常用的 Linux 操作系統(tǒng),就提供了 select、poll 和 epoll 三種編程模型,而在 Linux 上運(yùn)行的 Redis,通常就會(huì)采用其中的epoll模型來(lái)進(jìn)行網(wǎng)絡(luò)通訊。
為啥 Redis 通常會(huì)選擇 epoll 模型呢?這三種編程模型之間有什么區(qū)別?
要想理解 select、poll 和 epoll 的優(yōu)勢(shì),我們需要有個(gè)對(duì)比基礎(chǔ),也就是基本的 Socket 編程模型。所以接下來(lái),我們就先來(lái)了解下基本的 Socket 編程模型,以及它的不足之處。
為什么 Redis 不使用基本的 Socket 編程模型?
使用 Socket 模型實(shí)現(xiàn)網(wǎng)絡(luò)通信時(shí),需要經(jīng)過(guò)創(chuàng)建 Socket、監(jiān)聽端口、處理連接和讀寫請(qǐng)求等多個(gè)步驟,現(xiàn)在我們就來(lái)具體了解下這些步驟中的關(guān)鍵操作,以此幫助我們分析 Socket 模型中的不足。
首先,當(dāng)我們需要讓服務(wù)器端和客戶端進(jìn)行通信時(shí),可以在服務(wù)器端通過(guò)以下三步,來(lái)創(chuàng)建監(jiān)聽客戶端連接的監(jiān)聽套接字(Listening Socket):
- 調(diào)用 socket 函數(shù),創(chuàng)建一個(gè)套接字。我們通常把這個(gè)套接字稱為主動(dòng)套接字(Active Socket);
- 調(diào)用 bind 函數(shù),將主動(dòng)套接字和當(dāng)前服務(wù)器的 IP 和監(jiān)聽端口進(jìn)行綁定;
- 調(diào)用 listen 函數(shù),將主動(dòng)套接字轉(zhuǎn)換為監(jiān)聽套接字,開始監(jiān)聽客戶端的連接。
在完成上述三步之后,服務(wù)器端就可以接收客戶端的連接請(qǐng)求了。為了能及時(shí)地收到客戶端的連接請(qǐng)求,我們可以運(yùn)行一個(gè)循環(huán)流程,在該流程中調(diào)用 accept 函數(shù),用于接收客戶端連接請(qǐng)求。
這里你需要注意的是,accept 函數(shù)是阻塞函數(shù),也就是說(shuō),如果此時(shí)一直沒(méi)有客戶端連接請(qǐng)求,那么,服務(wù)器端的執(zhí)行流程會(huì)一直阻塞在 accept 函數(shù)。一旦有客戶端連接請(qǐng)求到達(dá),accept 將不再阻塞,而是處理連接請(qǐng)求,和客戶端建立連接,并返回已連接套接字(Connected Socket)。
最后,服務(wù)器端可以通過(guò)調(diào)用 recv 或 send 函數(shù),在剛才返回的已連接套接字上,接收并處理讀寫請(qǐng)求,或是將數(shù)據(jù)發(fā)送給客戶端。
代碼:
listenSocket = socket(); //調(diào)用socket系統(tǒng)調(diào)用創(chuàng)建一個(gè)主動(dòng)套接字 bind(listenSocket); //綁定地址和端口 listen(listenSocket); //將默認(rèn)的主動(dòng)套接字轉(zhuǎn)換為服務(wù)器使用的被動(dòng)套接字,也就是監(jiān)聽套接字 while(1) { //循環(huán)監(jiān)聽是否有客戶端連接請(qǐng)求到來(lái) connSocket = accept(listenSocket);//接受客戶端連接 recv(connSocket);//從客戶端讀取數(shù)據(jù),只能同時(shí)處理一個(gè)客戶端 send(connSocket);//給客戶端返回?cái)?shù)據(jù),只能同時(shí)處理一個(gè)客戶端 }
不過(guò),從上述代碼中,你可能會(huì)發(fā)現(xiàn),雖然它能夠?qū)崿F(xiàn)服務(wù)器端和客戶端之間的通信,但是程序每調(diào)用一次 accept 函數(shù),只能處理一個(gè)客戶端連接。因此,如果想要處理多個(gè)并發(fā)客戶端的請(qǐng)求,我們就需要使用多線程,來(lái)處理通過(guò) accept 函數(shù)建立的多個(gè)客戶端連接上的請(qǐng)求。
使用這種方法后,我們需要在 accept 函數(shù)返回已連接套接字后,創(chuàng)建一個(gè)線程,并將已連接套接字傳遞給創(chuàng)建的線程,由該線程負(fù)責(zé)這個(gè)連接套接字上后續(xù)的數(shù)據(jù)讀寫。同時(shí),服務(wù)器端的執(zhí)行流程會(huì)再次調(diào)用 accept 函數(shù),等待下一個(gè)客戶端連接。
多線程:
listenSocket = socket(); //調(diào)用socket系統(tǒng)調(diào)用創(chuàng)建一個(gè)主動(dòng)套接字 bind(listenSocket); //綁定地址和端口 listen(listenSocket); //將默認(rèn)的主動(dòng)套接字轉(zhuǎn)換為服務(wù)器使用的被動(dòng)套接字,也就是監(jiān)聽套接字 while(1) { //循環(huán)監(jiān)聽是否有客戶端連接請(qǐng)求到來(lái) connSocket = accept(listenSocket);//接受客戶端連接 pthread_create(processData, connSocket);//創(chuàng)建新線程對(duì)已連接套接字進(jìn)行處理 } processData(connSocket){ recv(connSocket);//從客戶端讀取數(shù)據(jù),只能同時(shí)處理一個(gè)客戶端 send(connSocket);//給客戶端返回?cái)?shù)據(jù),只能同時(shí)處理一個(gè)客戶端 }
雖然這種方法能提升服務(wù)器端的并發(fā)處理能力,但是,Redis 的主執(zhí)行流程是由一個(gè)線程在執(zhí)行,無(wú)法使用多線程的方式來(lái)提升并發(fā)處理能力。所以,該方法對(duì)redis并不起作用。
還有沒(méi)有什么其他方法,能幫助 Redis 提升并發(fā)客戶端的處理能力呢?這就要用到操作系統(tǒng)提供的IO多路復(fù)用功能。在基本的 Socket 編程模型中,accept 函數(shù)只能在一個(gè)監(jiān)聽套接字上監(jiān)聽客戶端的連接,recv 函數(shù)也只能在一個(gè)已連接套接字上,等待客戶端發(fā)送的請(qǐng)求。
因?yàn)?Linux 操作系統(tǒng)在實(shí)際應(yīng)用中比較廣泛,所以這節(jié)課,我們主要來(lái)學(xué)習(xí) Linux 上的 IO 多路復(fù)用機(jī)制。Linux 提供的 IO 多路復(fù)用機(jī)制主要有三種,分別是 select、poll 和 epoll。下面,我們就分別來(lái)學(xué)習(xí)下這三種機(jī)制的實(shí)現(xiàn)思路和使用方法。然后,我們?cè)賮?lái)看看,為什么 Redis 通常是選擇使用 epoll 這種機(jī)制來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)通信。
select 和 poll 機(jī)制實(shí)現(xiàn) IO 多路復(fù)用
首先,我們來(lái)了解下 select 機(jī)制的編程模型。
不過(guò)在具體學(xué)習(xí)之前,我們需要知道,對(duì)于一種 IO 多路復(fù)用機(jī)制來(lái)說(shuō),我們需要掌握哪些要點(diǎn),這樣可以幫助我們快速抓住不同機(jī)制的聯(lián)系與區(qū)別。其實(shí),當(dāng)我們學(xué)習(xí) IO 多路復(fù)用機(jī)制時(shí),我們需要能回答以下問(wèn)題:第一,多路復(fù)用機(jī)制會(huì)監(jiān)聽套接字上的哪些事件?第二,多路復(fù)用機(jī)制可以監(jiān)聽多少個(gè)套接字?第三,當(dāng)有套接字就緒時(shí),多路復(fù)用機(jī)制要如何找到就緒的套接字?
select機(jī)制
select 機(jī)制中的一個(gè)重要函數(shù)就是 select 函數(shù)。對(duì)于 select 函數(shù)來(lái)說(shuō),它的參數(shù)包括監(jiān)聽的文件描述符數(shù)量__nfds、、被監(jiān)聽描述符的三個(gè)集合readfds、writefds、exceptfds,以及監(jiān)聽時(shí)阻塞等待的超時(shí)時(shí)長(zhǎng)timeout。select函數(shù)原型:
int select(int __nfds, fd_set *__readfds, fd_set *__writefds, fd_set *__exceptfds, struct timeval *__timeout)
這里你需要注意的是,Linux 針對(duì)每一個(gè)套接字都會(huì)有一個(gè)文件描述符,也就是一個(gè)非負(fù)整數(shù),用來(lái)唯一標(biāo)識(shí)該套接字。所以,在多路復(fù)用機(jī)制的函數(shù)中,Linux 通常會(huì)用文件描述符作為參數(shù)。有了文件描述符,函數(shù)也就能找到對(duì)應(yīng)的套接字,進(jìn)而進(jìn)行監(jiān)聽、讀寫等操作。
select函數(shù)三個(gè)參數(shù)表示的是,被監(jiān)聽描述符的集合,其實(shí)就是被監(jiān)聽套接字的集合。那么,為什么會(huì)有三個(gè)集合呢?
剛才提出的第一個(gè)問(wèn)題相關(guān),也就是多路復(fù)用機(jī)制會(huì)監(jiān)聽套接字上的哪些事件。select 函數(shù)使用三個(gè)集合,表示監(jiān)聽的三類事件,分別是讀數(shù)據(jù)事件,寫數(shù)據(jù)事件,異常事件。
我們進(jìn)一步可以看到,參數(shù) readfds、writefds 和 exceptfds 的類型是 fd_set 結(jié)構(gòu)體,它主要定義部分如下所示。其中,fd_mask類型是 long int 類型的別名,__FD_SETSIZE 和 __NFDBITS 這兩個(gè)宏定義的大小默認(rèn)為 1024 和 32。
所以,fd_set 結(jié)構(gòu)體的定義,其實(shí)就是一個(gè) long int 類型的數(shù)組,該數(shù)組中一共有 32 個(gè)元素(1024/32=32),每個(gè)元素是 32 位(long int 類型的大?。?,而每一位可以用來(lái)表示一個(gè)文件描述符的狀態(tài)。了解了 fd_set 結(jié)構(gòu)體的定義,我們就可以回答剛才提出的第二個(gè)問(wèn)題了。select 函數(shù)對(duì)每一個(gè)描述符集合,都可以監(jiān)聽 1024 個(gè)描述符。
如何使用 select 機(jī)制來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)通信
首先,我們?cè)谡{(diào)用 select 函數(shù)前,可以先創(chuàng)建好傳遞給 select 函數(shù)的描述符集合,然后再創(chuàng)建監(jiān)聽套接字。而為了讓創(chuàng)建的監(jiān)聽套接字能被 select 函數(shù)監(jiān)控,我們需要把這個(gè)套接字的描述符加入到創(chuàng)建好的描述符集合中。
然后,我們就可以調(diào)用 select 函數(shù),并把創(chuàng)建好的描述符集合作為參數(shù)傳遞給 select 函數(shù)。程序在調(diào)用 select 函數(shù)后,會(huì)發(fā)生阻塞。而當(dāng) select 函數(shù)檢測(cè)到有描述符就緒后,就會(huì)結(jié)束阻塞,并返回就緒的文件描述符個(gè)數(shù)。
那么此時(shí),我們就可以在描述符集合中查找哪些描述符就緒了。然后,我們對(duì)已就緒描述符對(duì)應(yīng)的套接字進(jìn)行處理。比如,如果是 readfds 集合中有描述符就緒,這就表明這些就緒描述符對(duì)應(yīng)的套接字上,有讀事件發(fā)生,此時(shí),我們就在該套接字上讀取數(shù)據(jù)。
而因?yàn)?select 函數(shù)一次可以監(jiān)聽 1024 個(gè)文件描述符的狀態(tài),所以 select 函數(shù)在返回時(shí),也可能會(huì)一次返回多個(gè)就緒的文件描述符。這樣一來(lái),我們就可以使用一個(gè)循環(huán)流程,依次對(duì)就緒描述符對(duì)應(yīng)的套接字進(jìn)行讀寫或異常處理操作。
select函數(shù)有兩個(gè)不足
首先,select 函數(shù)對(duì)單個(gè)進(jìn)程能監(jiān)聽的文件描述符數(shù)量是有限制的,它能監(jiān)聽的文件描述符個(gè)數(shù)由 __FD_SETSIZE 決定,默認(rèn)值是 1024。
其次,當(dāng) select 函數(shù)返回后,我們需要遍歷描述符集合,才能找到具體是哪些描述符就緒了。這個(gè)遍歷過(guò)程會(huì)產(chǎn)生一定開銷,從而降低程序的性能。
poll機(jī)制
poll 機(jī)制的主要函數(shù)是 poll 函數(shù),我們先來(lái)看下它的原型定義,如下所示:
int poll(struct pollfd *__fds, nfds_t __nfds, int __timeout)
其中,參數(shù) *__fds 是 pollfd 結(jié)構(gòu)體數(shù)組,參數(shù) __nfds 表示的是 *__fds 數(shù)組的元素個(gè)數(shù),而 __timeout 表示 poll 函數(shù)阻塞的超時(shí)時(shí)間。
pollfd 結(jié)構(gòu)體里包含了要監(jiān)聽的描述符,以及該描述符上要監(jiān)聽的事件類型。這個(gè)我們可以從 pollfd 結(jié)構(gòu)體的定義中看出來(lái),如下所示。pollfd 結(jié)構(gòu)體中包含了三個(gè)成員變量 fd、events 和 revents,分別表示要監(jiān)聽的文件描述符、要監(jiān)聽的事件類型和實(shí)際發(fā)生的事件類型。
pollfd 結(jié)構(gòu)體中要監(jiān)聽和實(shí)際發(fā)生的事件類型,是通過(guò)以下三個(gè)宏定義來(lái)表示的,分別是 POLLRDNORM、POLLWRNORM 和 POLLERR,它們分別表示可讀、可寫和錯(cuò)誤事件。
了解了 poll 函數(shù)的參數(shù)后,我們來(lái)看下如何使用 poll 函數(shù)完成網(wǎng)絡(luò)通信。這個(gè)流程主要可以分成三步:
- 第一步,創(chuàng)建 pollfd 數(shù)組和監(jiān)聽套接字,并進(jìn)行綁定;
- 第二步,將監(jiān)聽套接字加入 pollfd 數(shù)組,并設(shè)置其監(jiān)聽讀事件,也就是客戶端的連接請(qǐng)求;
- 第三步,循環(huán)調(diào)用 poll 函數(shù),檢測(cè) pollfd 數(shù)組中是否有就緒的文件描述符。
而在第三步的循環(huán)過(guò)程中,其處理邏輯又分成了兩種情況:
如果是連接套接字就緒,這表明是有客戶端連接,我們可以調(diào)用 accept 接受連接,并創(chuàng)建已連接套接字,并將其加入 pollfd 數(shù)組,并監(jiān)聽讀事件;
如果是已連接套接字就緒,這表明客戶端有讀寫請(qǐng)求,我們可以調(diào)用 recv/send 函數(shù)處理讀寫請(qǐng)求。
其實(shí),和 select 函數(shù)相比,poll 函數(shù)的改進(jìn)之處主要就在于,它允許一次監(jiān)聽超過(guò) 1024 個(gè)文件描述符。但是當(dāng)調(diào)用了 poll 函數(shù)后,我們?nèi)匀恍枰闅v每個(gè)文件描述符,檢測(cè)該描述符是否就緒,然后再進(jìn)行處理。
epoll機(jī)制
首先,epoll 機(jī)制是使用 epoll_event 結(jié)構(gòu)體,來(lái)記錄待監(jiān)聽的文件描述符及其監(jiān)聽的事件類型的,這和 poll 機(jī)制中使用 pollfd 結(jié)構(gòu)體比較類似。
那么,對(duì)于 epoll_event 結(jié)構(gòu)體來(lái)說(shuō),其中包含了 epoll_data_t 聯(lián)合體變量,以及整數(shù)類型的 events 變量。epoll_data_t 聯(lián)合體中有記錄文件描述符的成員變量 fd,而 events 變量會(huì)取值使用不同的宏定義值,來(lái)表示 epoll_data_t 變量中的文件描述符所關(guān)注的事件類型,比如一些常見(jiàn)的事件類型包括以下這幾種。
- EPOLLIN:讀事件,表示文件描述符對(duì)應(yīng)套接字有數(shù)據(jù)可讀。
- EPOLLOUT:寫事件,表示文件描述符對(duì)應(yīng)套接字有數(shù)據(jù)要寫。
- EPOLLERR:錯(cuò)誤事件,表示文件描述符對(duì)于套接字出錯(cuò)。
在使用 select 或 poll 函數(shù)的時(shí)候,創(chuàng)建好文件描述符集合或 pollfd 數(shù)組后,就可以往數(shù)組中添加我們需要監(jiān)聽的文件描述符。
但是對(duì)于 epoll 機(jī)制來(lái)說(shuō),我們則需要先調(diào)用 epoll_create 函數(shù),創(chuàng)建一個(gè) epoll 實(shí)例。這個(gè) epoll 實(shí)例內(nèi)部維護(hù)了兩個(gè)結(jié)構(gòu),分別是記錄要監(jiān)聽的文件描述符和已經(jīng)就緒的文件描述符,,而對(duì)于已經(jīng)就緒的文件描述符來(lái)說(shuō),它們會(huì)被返回給用戶程序進(jìn)行處理。
所以,我們?cè)谑褂?epoll 機(jī)制時(shí),就不用像使用 select 和 poll 一樣,遍歷查詢哪些文件描述符已經(jīng)就緒了。這樣一來(lái), epoll 的效率就比 select 和 poll 有了更高的提升。
在創(chuàng)建了 epoll 實(shí)例后,我們需要再使用 epoll_ctl 函數(shù),給被監(jiān)聽的文件描述符添加監(jiān)聽事件類型,以及使用 epoll_wait 函數(shù)獲取就緒的文件描述符。
了解了 epoll 函數(shù)的使用方法了。實(shí)際上,也正是因?yàn)?epoll 能自定義監(jiān)聽的描述符數(shù)量,以及可以直接返回就緒的描述符,Redis 在設(shè)計(jì)和實(shí)現(xiàn)網(wǎng)絡(luò)通信框架時(shí),就基于 epoll 機(jī)制中的 epoll_create、epoll_ctl 和 epoll_wait 等函數(shù)和讀寫事件,進(jìn)行了封裝開發(fā),實(shí)現(xiàn)了用于網(wǎng)絡(luò)通信的事件驅(qū)動(dòng)框架,從而使得 Redis 雖然是單線程運(yùn)行,但是仍然能高效應(yīng)對(duì)高并發(fā)的客戶端訪問(wèn)。
Reactor 模型的工作機(jī)制
Reactor 模型就是網(wǎng)絡(luò)服務(wù)器端用來(lái)處理高并發(fā)網(wǎng)絡(luò) IO 請(qǐng)求的一種編程模型,模型特征:
- 三類處理事件,即連接事件、寫事件、讀事件;
- 三個(gè)關(guān)鍵角色,即 reactor、acceptor、handler。
Reactor 模型處理的是客戶端和服務(wù)器端的交互過(guò)程,而這三類事件正好對(duì)應(yīng)了客戶端和服務(wù)器端交互過(guò)程中,不同類請(qǐng)求在服務(wù)器端引發(fā)的待處理事件:
當(dāng)一個(gè)客戶端要和服務(wù)器端進(jìn)行交互時(shí),客戶端會(huì)向服務(wù)器端發(fā)送連接請(qǐng)求,以建立連接,這就對(duì)應(yīng)了服務(wù)器端的一個(gè)鏈接事件
一旦連接建立后,客戶端會(huì)給服務(wù)器端發(fā)送讀請(qǐng)求,以便讀取數(shù)據(jù)。服務(wù)器端在處理讀請(qǐng)求時(shí),需要向客戶端寫回?cái)?shù)據(jù),這對(duì)應(yīng)了服務(wù)器端的寫事件
無(wú)論客戶端給服務(wù)器端發(fā)送讀或?qū)懻?qǐng)求,服務(wù)器端都需要從客戶端讀取請(qǐng)求內(nèi)容,所以在這里,讀或?qū)懻?qǐng)求的讀取就對(duì)應(yīng)了服務(wù)器端的讀事件
三個(gè)關(guān)鍵角色:
首先,連接事件由 acceptor 來(lái)處理,負(fù)責(zé)接收連接;acceptor 在接收連接后,會(huì)創(chuàng)建 handler,用于網(wǎng)絡(luò)連接上對(duì)后續(xù)讀寫事件的處理;
其次,讀寫事件由 handler 處理;
最后,在高并發(fā)場(chǎng)景中,連接事件、讀寫事件會(huì)同時(shí)發(fā)生,所以,我們需要有一個(gè)角色專門監(jiān)聽和分配事件,這就是 reactor 角色。當(dāng)有連接請(qǐng)求時(shí),reactor 將產(chǎn)生的連接事件交由 acceptor 處理;當(dāng)有讀寫請(qǐng)求時(shí),reactor 將讀寫事件交由 handler 處理。
那么,現(xiàn)在我們已經(jīng)知道,這三個(gè)角色是圍繞事件的監(jiān)聽、轉(zhuǎn)發(fā)和處理來(lái)進(jìn)行交互的,那么在編程時(shí),我們又該如何實(shí)現(xiàn)這三者的交互呢?這就離不開事件驅(qū)動(dòng)。
所謂的事件驅(qū)動(dòng)框架,就是在實(shí)現(xiàn) Reactor 模型時(shí),需要實(shí)現(xiàn)的代碼整體控制邏輯。簡(jiǎn)單來(lái)說(shuō),事件驅(qū)動(dòng)框架包括了兩部分:一是事件初始化,二事件捕獲,分化和處理主循環(huán)。
事件初始化是在服務(wù)器程序啟動(dòng)時(shí)就執(zhí)行的,它的作用主要是創(chuàng)建需要監(jiān)聽的事件類型,以及該類事件對(duì)應(yīng)的 handler。而一旦服務(wù)器完成初始化后,事件初始化也就相應(yīng)完成了,服務(wù)器程序就需要進(jìn)入到事件捕獲、分發(fā)和處理的主循環(huán)中。
用while循環(huán)來(lái)作為這個(gè)主循環(huán)。然后在這個(gè)主循環(huán)中,我們需要捕獲發(fā)生的事件、判斷事件類型,并根據(jù)事件類型,調(diào)用在初始化時(shí)創(chuàng)建好的事件 handler 來(lái)實(shí)際處理事件。
比如說(shuō),當(dāng)有連接事件發(fā)生時(shí),服務(wù)器程序需要調(diào)用 acceptor 處理函數(shù),創(chuàng)建和客戶端的連接。而當(dāng)有讀事件發(fā)生時(shí),就表明有讀或?qū)懻?qǐng)求發(fā)送到了服務(wù)器端,服務(wù)器程序就要調(diào)用具體的請(qǐng)求處理函數(shù),從客戶端連接中讀取請(qǐng)求內(nèi)容,進(jìn)而就完成了讀事件的處理。
Reactor 模型的基本工作機(jī)制:客戶端的不同類請(qǐng)求會(huì)在服務(wù)器端觸發(fā)連接、讀、寫三類事件,這三類事件的監(jiān)聽、分發(fā)和處理又是由 reactor、acceptor、handler 三類角色來(lái)完成的,然后這三類角色會(huì)通過(guò)事件驅(qū)動(dòng)框架來(lái)實(shí)現(xiàn)交互和事件處理。
到此這篇關(guān)于淺談Redis的事件驅(qū)動(dòng)模型的文章就介紹到這了,更多相關(guān)Redis 事件驅(qū)動(dòng)模型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis與本地緩存的結(jié)合實(shí)現(xiàn)
我們開發(fā)中經(jīng)常用到Redis作為緩存,本文主要介紹了Redis與本地緩存的結(jié)合實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07基于redis 7.2.3的makefile源碼解讀學(xué)習(xí)
這篇文章主要為大家介紹了基于redis 7.2.3的makefile源碼解讀學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12Redis Template實(shí)現(xiàn)分布式鎖的實(shí)例代碼
使用Redis的SETNX命令獲取分布式鎖的步驟,接下來(lái)通過(guò)本文給大家介紹Redis Template實(shí)現(xiàn)分布式鎖的實(shí)例代碼,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2018-09-09redis事務(wù)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了redis事務(wù),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08CentOS系統(tǒng)中Redis數(shù)據(jù)庫(kù)的安裝配置指南
Redis是一個(gè)基于主存存儲(chǔ)的數(shù)據(jù)庫(kù),性能很強(qiáng),這里我們就來(lái)看一下CentOS系統(tǒng)中Redis數(shù)據(jù)庫(kù)的安裝配置指南,包括將Redis作為系統(tǒng)服務(wù)運(yùn)行的技巧等,需要的朋友可以參考下2016-06-06Redis服務(wù)器的啟動(dòng)過(guò)程分析
這篇文章主要介紹了Redis服務(wù)器的啟動(dòng)過(guò)程分析,本文講解了初始化Redis服務(wù)器全局配置、加載配置文件、初始化服務(wù)器、加載數(shù)據(jù)、開始網(wǎng)絡(luò)監(jiān)聽等內(nèi)容,需要的朋友可以參考下2015-04-04深入解析Redis的LRU與LFU算法實(shí)現(xiàn)
這篇文章主要重點(diǎn)介紹了Redis的LRU與LFU算法實(shí)現(xiàn),并分析總結(jié)了兩種算法的實(shí)現(xiàn)效果以及存在的問(wèn)題,并闡述其優(yōu)劣特性,感興趣的小伙伴跟著小編一起來(lái)看看吧2023-07-07