閱讀瀏覽器源碼的好工具:SourceGraph
Redis通過將對應(yīng)事件注冊到eventloop中絮重,然后不斷循環(huán)檢測有無事件觸發(fā)挂签。
aeProcessEvents代表處理一次循環(huán)事件處理:
- 取出最近的一次定時器事件销凑。
- 計算該超時定時事件還有多久才可以觸發(fā)呜达,若已超時則設(shè)置t為0,其中t為第3步的超時閾值教沾。
- 調(diào)用select或者epoll等待網(wǎng)絡(luò)事件觸發(fā)闯两,或者超時钟病。
- 處理觸發(fā)的各個事件,包括網(wǎng)絡(luò)事件和超時事件姑子。
也就是說每次循環(huán)事件處理中乎婿,網(wǎng)絡(luò)IO讀寫事件的處理過程中,會順便檢測處理定時事件街佑。
關(guān)于第2點的額外說明谢翎,每次事件循環(huán)處理周期當(dāng)中,獲取距離當(dāng)前的這個時間間隔值沐旨。拿來做什么用呢森逮?
為了避免“忙等待”,我們在檢查FD的IO讀寫狀態(tài)時(select或者epoll)磁携,都會采用阻塞的方式褒侧,如果沒有可讀可寫的FD,就一直阻塞著等待。但是璃搜,我還有定時器事件要處理啊拖吼,如果一直沒有IO事件,那我定時器事件不是一直沒法處理么这吻?所以吊档,我們會給select或者epoll傳入一個阻塞的超時時間,超過這個時間唾糯,都給我返回怠硼。 下面獲取的這個值,就是用于設(shè)置阻塞超時時間的移怯。這樣做香璃,既可以避免非阻塞式的忙等待,又可以保證定時器事件能夠按時得到處理舟误。 其實這種處理方式非常普遍葡秒,以C為開發(fā)語言的很多服務(wù)型軟件都是這樣玩的。
Redis初始化服務(wù)的時候嵌溢,會先初始化一個Eventloop
server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);
目前eventloop支持注冊:
- 定時器事件
- 網(wǎng)絡(luò)IO讀寫事件
其中網(wǎng)絡(luò)IO讀寫事件的fd看做下標(biāo)的原因是:Linux內(nèi)核會給每個進程維護一個文件描述符表眯牧。
而POSIX標(biāo)準對于文件描述符進行了以下約束:
- fd為0、1赖草、2分別表示標(biāo)準輸入学少、標(biāo)準輸出和錯誤輸出。
- 每次新打開的fd秧骑,必須使用當(dāng)前進程中最小可用的文件描述符版确。
因此注冊事件下標(biāo)能與系統(tǒng)fd形成有效重合復(fù)用。
其中我們關(guān)注的網(wǎng)絡(luò)IO事件數(shù)據(jù)結(jié)構(gòu)如下:一般情況下乎折,Redis會先處理讀事件(AE_READABLE)绒疗,再處理寫事件(AE_WRITABLE)。 這個順序安排其實也算是一點小優(yōu)化笆檀,先讀后寫可以讓一個請求的處理和回包都是在同一次循環(huán)里面忌堂,使得請求可以盡快地回包。
網(wǎng)絡(luò)IO事件注冊的時候酗洒,除了正常的讀寫事件外士修,還可以注冊一個AE_BARRIER事件,這個事件就是會影響到先讀后寫的處理順序樱衷。 如果某個fd的mask包含AE_BARRIER棋嘲,那它的處理順序會是先寫后讀。