一岁歉、事件
Redis服務(wù)器是一個(gè)事件驅(qū)動(dòng)程序,服務(wù)器需要處理兩類事件:
1)文件事件(file event)Redis服務(wù)器通過套接字與客戶端(或者其它Redis服務(wù)器)進(jìn)行連接姑蓝,文件事件就是服務(wù)器對(duì)套接字操作的抽象鹅心。服務(wù)器與客戶端的通信會(huì)產(chǎn)生相應(yīng)的文件事件,服務(wù)器通過監(jiān)聽并處理這些事件來完成一系列網(wǎng)絡(luò)通信操作纺荧。
2)時(shí)間事件(time event)Redis服務(wù)器中的一些操作(比如serverCron函數(shù))需要在給定的時(shí)間點(diǎn)執(zhí)行旭愧,時(shí)間事件就是服務(wù)器對(duì)這類定時(shí)操作的抽象颅筋。
1、文件事件
Redis基于Reactor 模式開發(fā)了自己的網(wǎng)絡(luò)事件處理器:這個(gè)處理器被稱為文件事件處理器(file event handler):
1)文件事件處理器使用I/O多路復(fù)用程序來同時(shí)監(jiān)聽多個(gè)套接字输枯,并根據(jù)套接字目前執(zhí)行的任務(wù)來為套接字關(guān)聯(lián)不同的事件處理器议泵。
2)當(dāng)被監(jiān)聽的套接字準(zhǔn)備好執(zhí)行連接應(yīng)答(accept)、讀忍蚁ā(read)先口、寫入(write)、關(guān)閉(close)等操作時(shí)瞳收,與操作相對(duì)應(yīng)的文件事件就會(huì)產(chǎn)生碉京,這時(shí)文件事件處理器就會(huì)調(diào)用套接字之前關(guān)聯(lián)的事件處理器來處理這些事件。
1.1螟深、文件事件處理器的構(gòu)成
四個(gè)組成部分:套接字谐宙、I/O多路復(fù)用程序、文件事件分派器界弧、事件處理器
????????盡管多個(gè)文件事件可能會(huì)并發(fā)出現(xiàn)凡蜻,但I(xiàn)/O多路復(fù)用程序總是將所有產(chǎn)生事件的套接字都放到一個(gè)隊(duì)列里面,然后通過這個(gè)隊(duì)列垢箕,以有序(sequentially)划栓、同步(synchronously)、每次一個(gè)套接字的方式向文件事件分派器傳送套接字舰讹。當(dāng)上一個(gè)套接字產(chǎn)生的事件被處理完畢之后茅姜、I/O多路復(fù)用程序才會(huì)繼續(xù)向文件事件分派器傳送下一個(gè)套接字。
1.2月匣、I/O多路復(fù)用程序的實(shí)現(xiàn)
? ? ? ? Redis的I/O多路復(fù)用程序的所有功能通過包裝常見的select钻洒、epoll、evport和kqueue這些I/O多路復(fù)用函數(shù)庫來實(shí)現(xiàn)锄开,每個(gè)I/O多路復(fù)用函數(shù)庫在Redis源碼中都對(duì)應(yīng)一個(gè)單獨(dú)的文件素标。
? ? ? ? 因?yàn)镽edis為每個(gè)I/O多路復(fù)用函數(shù)庫都實(shí)現(xiàn)了相同的API,所以I/O多路復(fù)用程序的底層實(shí)現(xiàn)是可以互換的。
1.3萍悴、事件類型
????????ae.h/AE_READABLE事件和ae.h/AE_WRITABLE事件
1)當(dāng)套接字變得可讀時(shí)(客戶端執(zhí)行write操作头遭、close操作),或者有新的可應(yīng)答(acceptable)套接字出現(xiàn)時(shí)(客戶端對(duì)服務(wù)器的監(jiān)聽套接字執(zhí)行connect操作)癣诱,套接字產(chǎn)生AE_READABLE事件计维。
2)當(dāng)套接字變得可寫時(shí)(客戶端執(zhí)行read操作),套接字產(chǎn)生AE_WRITABLE事件撕予。
? ? ? ? I/O多路復(fù)用程序允許服務(wù)器同時(shí)監(jiān)聽套接字的AE_READABLE事件和AE_WRITABLE事件鲫惶,如果一個(gè)套接字同時(shí)產(chǎn)生了這兩種事件、文件事件分派器優(yōu)先處理AE_READABLE事件实抡、等到AE_READABLE事件處理完之后欠母,才處理AE_WRITABLE事件欢策。優(yōu)先處理讀事件
1.4、API
? ? ? ? ae.c/aeCreateFileEvent 函數(shù)接受一個(gè)套接字描述符赏淌、一個(gè)事件類型踩寇,一個(gè)時(shí)間處理器作為參數(shù),將給定套接字的給定事件加入到I/O多路復(fù)用程序的監(jiān)聽范圍內(nèi)六水,并對(duì)事件和事件處理器進(jìn)行關(guān)聯(lián)俺孙。
? ? ? ? ae.c/aeDeleteFileEvent 函數(shù)?接受一個(gè)套接字描述符、一個(gè)監(jiān)聽事件類型 作為參數(shù)缩擂,讓I/O多路復(fù)用程序取消對(duì)給定套接字的給定事件的監(jiān)聽鼠冕,并取消事件和事件處理器之間的關(guān)聯(lián)。
? ? ? ? ae.c/aeGetFileEvents 函數(shù)?接受一個(gè)套接字描述符胯盯,返回該套接字正在被監(jiān)聽的事件類型:
? ? 1)如果套接字沒有被任何事件監(jiān)聽,返回AE_NONE
? ? 2)如果套接字的讀事件正在被監(jiān)聽计露,返回AE_READABLE
? ? 3)如果套接字的寫時(shí)間正在被監(jiān)聽博脑,返回AE_WRITABLE
? ? 4)如果套接字的讀事件和寫事件同時(shí)被監(jiān)聽,返回AE_READABLE | AE_WRITABLE
? ? ? ? ae.c/aeWait 函數(shù)?接受一個(gè)套接字描述符票罐、一個(gè)事件類型叉趣、一個(gè)毫秒數(shù)作為參數(shù),在給定的時(shí)間內(nèi)阻塞并等待套接字的給定類型事件產(chǎn)生该押,當(dāng)事件成功產(chǎn)生疗杉,或者等待超時(shí)之后,函數(shù)返回蚕礼。
? ? ? ? ae.c/aeApiPoll 函數(shù)?接受一個(gè)sys/time.h/struct timeval結(jié)構(gòu)體為參數(shù)烟具,并在指定時(shí)間內(nèi),阻塞并等待所有被aeCreateFileEvent函數(shù)設(shè)置為監(jiān)聽狀態(tài)的套接字產(chǎn)生的文件事件奠蹬,當(dāng)有至少一個(gè)事件產(chǎn)生朝聋,或者等待超時(shí)后,函數(shù)返回囤躁。
? ? ? ? ae.c/aeProcessEvents 函數(shù) 是文件事件分派器冀痕,它先調(diào)用aeApiPoll 函數(shù)來等待事件產(chǎn)生,然后遍歷所有已產(chǎn)生的事件狸演,并調(diào)用相應(yīng)的事件處理器來處理這些事件言蛇。
? ? ? ? ae.c/aeGetApiName 函數(shù)?返回I/O多路復(fù)用程序底層所使用的I/O多路復(fù)用函數(shù)庫的名稱:返回"epoll"表示底層為epoll函數(shù)庫,返回"select"表示底層為select函數(shù)庫宵距。
1.5腊尚、文件事件處理器
????????Redis為文件事件編寫了多個(gè)處理器。
1)連接應(yīng)答處理器
? ??????networking.c/acceptTcpHandler 函數(shù) 是Redis的連接應(yīng)答處理器消玄,這個(gè)處理器用于對(duì)連接服務(wù)器監(jiān)聽套接字的客戶端進(jìn)行應(yīng)答跟伏。
2)命令請(qǐng)求處理器
? ? ? ? networking.c/readQueryFromClient 函數(shù) 是Redis的命令請(qǐng)求處理器丢胚,負(fù)責(zé)從套接字中讀入客戶端發(fā)送的命令請(qǐng)求內(nèi)容。
3)命令回復(fù)處理器
? ??????networking.c/sendReplyToClient 函數(shù) 是Redis的命令回復(fù)處理器受扳,負(fù)責(zé)將服務(wù)器執(zhí)行命令后得到的命令回復(fù)通過套接字返回客戶端携龟。
2、時(shí)間事件
? ? ? ? Redis時(shí)間事件分為兩類:定時(shí)事件勘高、周期性事件
? ? ? ? 一個(gè)時(shí)間事件主要由三個(gè)屬性組成:
? ? ? ? 1)id: 服務(wù)器為時(shí)間事件創(chuàng)建的全局唯一ID(標(biāo)識(shí)號(hào))峡蟋,從小到大順序遞增
? ? ? ? 2)when: 毫秒精度的UNIX時(shí)間戳,記錄時(shí)間事件的到達(dá)時(shí)間
? ? ? ? 3)timeProc: 時(shí)間事件處理器华望,一個(gè)函數(shù)
? ? ? ? 一個(gè)時(shí)間事件的類型取決于時(shí)間事件處理器的返回值:
? ? ? ? 1)返回 ae.h/AE_NOMORE 的整數(shù)值蕊蝗,為定時(shí)事件
? ? ? ? 2)返回 非ae.h/AE_NOMORE 的整數(shù)值,為周期性事件
2.1赖舟、實(shí)現(xiàn)
? ? ? ? 服務(wù)器將所有時(shí)間事件放在一個(gè)無序鏈表中蓬戚,每當(dāng)時(shí)間事件執(zhí)行器運(yùn)行時(shí),就遍歷整個(gè)鏈表宾抓,查找所有已到達(dá)的時(shí)間事件子漩,并調(diào)用相應(yīng)的時(shí)間事件處理器。
2.2石洗、API
? ? ? ? ae.c/aeCreateTimeEvent 函數(shù) 接受一個(gè)毫秒值和一個(gè)時(shí)間事件處理器作為參數(shù)幢泼,將一個(gè)時(shí)間事件添加到服務(wù)器。
? ??????ae.c/aeDeleteFileEvent 函數(shù)?接受一個(gè)時(shí)間事件ID作為參數(shù)讲衫,從服務(wù)器中刪除該ID所對(duì)應(yīng)的的時(shí)間事件缕棵。
? ??????ae.c/aeSerachNearestTimer 函數(shù)?返回到達(dá)時(shí)間距離當(dāng)前時(shí)間最近的時(shí)間事件。
? ??????ae.c/processTimeEvents 函數(shù)?是時(shí)間事件的執(zhí)行器涉兽,會(huì)遍歷所有已到達(dá)的時(shí)間事件招驴,并調(diào)用這些事件的處理器德撬。
2.3家破、時(shí)間事件應(yīng)用實(shí)例:serverCron函數(shù)
????????Redis定期操作由redis.c/serverCron函數(shù)負(fù)責(zé)執(zhí)行秦躯,主要工作包括:
? ? ? ? 1)更新服務(wù)器各類統(tǒng)計(jì)信息孵睬,如時(shí)間朗伶、內(nèi)存占用芭毙、數(shù)據(jù)庫占用
? ? ? ? 2)清理數(shù)據(jù)庫中的過期鍵值對(duì)
? ? ? ? 3)關(guān)閉和清理連接失效的客戶端
? ? ? ? 4)嘗試進(jìn)行AOF或RDB持久化操作
? ? ? ? 5)如果是主服務(wù)器航瞭,對(duì)從服務(wù)器進(jìn)行定期同步
? ? ? ? 6)如果處于集群模式携茂,對(duì)集群進(jìn)行定期同步和連接測(cè)試? ? ? ??
3袋倔、事件的調(diào)度與執(zhí)行
? ? ? ? 事件的調(diào)度和執(zhí)行由ae.c/aeProcessEvents函數(shù)負(fù)責(zé)
二雕蔽、客戶端
1、客戶端屬性
? ? ? ? 客戶端包含的屬性分為兩類:
? ? ? ? 1)通用屬性宾娜,無論客戶端執(zhí)行的是什么工作批狐,都要用到的屬性
? ? ? ? 2)非通用屬性,和特定功能相關(guān)的屬性,如操作數(shù)據(jù)庫需要用到的db屬性和dictid屬性嚣艇,執(zhí)行事務(wù)需要的mstate屬性承冰,以及實(shí)行WATCH命令用到的watch_keys屬性
1.1、套接字描述符 fd
? ? ? ? fd 屬性記錄了客戶端正在使用的套接字描述符,可以是-1或者大于-1的整數(shù):
? ? ? ? 1)偽客戶端的fd屬性值為-1
? ? ? ? 2)普通客戶端的fd屬性為大于-1的整數(shù)
1.2食零、名字 name
? ? ? ? 默認(rèn)情況下困乒,一個(gè)連接到服務(wù)器的客戶端沒有名字,使用CLIENT setname 命令可以為客戶端設(shè)置一個(gè)名字
1.3贰谣、標(biāo)志 flags
????????flags 屬性記錄了客戶端的角色娜搂,以及客戶端目前所處的狀態(tài),可以是單個(gè)標(biāo)志吱抚,也可以是多個(gè)標(biāo)志的二進(jìn)制 或運(yùn)算值
? ? ? ? flags = <flag>
? ? ? ? flags = <flag1> | <flag2>| ...? ? ? ??
? ? ? ? 每個(gè)標(biāo)志使用一個(gè)常量表示百宇,一部分標(biāo)志記錄客戶端的角色,另一部分記錄客戶端目前所處狀態(tài)
1.4秘豹、輸入緩沖區(qū) querybuf
????????客戶端輸入緩沖區(qū)用于保存 客戶端發(fā)送過來的命令請(qǐng)求
1.5携御、命令參數(shù) argv 與命令參數(shù)個(gè)數(shù) argc
? ? ? ? 在服務(wù)器將客戶端發(fā)送的命令請(qǐng)求保存到客戶端狀態(tài)的querybuf屬性之后,服務(wù)器將對(duì)命令請(qǐng)求進(jìn)行分析憋肖,并將得出的命令參數(shù)及命令參數(shù)個(gè)數(shù)分別保存到客戶端狀態(tài)的argv屬性 和argc屬性因痛。
? ? ? ? argv是一個(gè)數(shù)組,每一項(xiàng)都是一個(gè)字符串對(duì)象岸更,argv[0] 是要執(zhí)行的命令,之后的其他項(xiàng)是命令參數(shù)
? ? ? ? argc屬性記錄argv數(shù)組的長度
1.6膊升、命令的實(shí)現(xiàn)函數(shù)cmd
? ? ? ? 服務(wù)器根據(jù)argv[0] 的值怎炊,在命令表中查找命令所對(duì)應(yīng)的命令實(shí)現(xiàn)函數(shù),命令表是一個(gè)字典廓译,鍵是SDS結(jié)構(gòu)评肆,保存命令的名稱,字典的值是命令所對(duì)應(yīng)的redisCommand結(jié)構(gòu)非区,保存命令的實(shí)現(xiàn)函數(shù)瓜挽、命令標(biāo)志、命令應(yīng)該給定的參數(shù)個(gè)數(shù)征绸、命令的總執(zhí)行次數(shù)和總消耗時(shí)長等信息久橙。
1.7、輸出緩沖區(qū)
? ? ? ? 執(zhí)行命令所得的命令回復(fù)會(huì)保存在客戶端狀態(tài)的輸出緩沖區(qū)里管怠,每個(gè)客戶端有兩個(gè)輸出緩存區(qū)可用淆衷,一個(gè)大小固定,另一個(gè)大小可變渤弛。
? ? ? ? 1)固定大小的緩沖區(qū)用于保存長度較小的回復(fù)祝拯,如OK、簡短的字符串值她肯、整數(shù)值佳头、錯(cuò)誤回復(fù)等
? ? ? ? buf是一個(gè)大小為REDIS_REPLY_CHUNK_BYTES字節(jié)的字節(jié)數(shù)組鹰贵,bufpos記錄buf數(shù)組目前已使用的字節(jié)數(shù)量。
? ? ? ? REDIS_REPLY_CHUNK_BYTES 默認(rèn)值為16*1024 康嘉, buf數(shù)組默認(rèn)大小為16KB碉输。
? ??????2)可變大小的緩沖區(qū)用于保存長度比較大的回復(fù)。
? ? ? ? 當(dāng)buf數(shù)組空間用完凄鼻,或者回復(fù)太大而沒有辦法放進(jìn)buf數(shù)組內(nèi)腊瑟,服務(wù)器會(huì)使用可變大小緩沖區(qū)。
? ? ? ? 可變大小緩沖區(qū)有reply鏈表和一個(gè)或多個(gè)字符串對(duì)象組成块蚌。
1.8闰非、身份驗(yàn)證 authenticated
? ??????authenticated 值為0 ,表示客戶端未通過身份驗(yàn)證峭范;為1财松,表示客戶端已經(jīng)通過身份驗(yàn)證。
? ? ? ??authenticated 屬性僅在服務(wù)器啟動(dòng)身份驗(yàn)證功能時(shí)使用
1.9纱控、時(shí)間
? ? ? ? ctime 記錄創(chuàng)建客戶端的時(shí)間
? ? ? ? lastinteraction 屬性記錄客戶端與服務(wù)器最后進(jìn)行互動(dòng)的時(shí)間
? ? ? ? obuf_soft_limit_reached_time 屬性記錄了輸出緩沖區(qū)第一次到達(dá)軟性限制的時(shí)間
2辆毡、客戶端的創(chuàng)建與關(guān)閉
2.1、創(chuàng)建普通客戶端
? ? ? ? 客戶端通過網(wǎng)絡(luò)連接服務(wù)器甜害,在客戶端使用connect函數(shù)連接到服務(wù)器時(shí)舶掖,服務(wù)器會(huì)調(diào)用連接事件處理器,為客戶端創(chuàng)建相應(yīng)的客戶端狀態(tài)尔店,并將這個(gè)新的客戶端狀態(tài)添加到服務(wù)器狀態(tài)結(jié)構(gòu)clients鏈表的末尾眨攘。? ? ? ??
2.2、關(guān)閉普通客戶端
1)客戶單進(jìn)程退出或者被殺死嚣州,連接關(guān)閉鲫售,從而造成客戶端被關(guān)閉
2)客戶端向服務(wù)器發(fā)送帶有不符合協(xié)議格式的命令請(qǐng)求,客戶端也會(huì)被服務(wù)器關(guān)閉
3)客戶端成為了 CLIENT KILL命令的目標(biāo)
4)用戶為服務(wù)器設(shè)置了timeout選項(xiàng)该肴,當(dāng)客戶端空轉(zhuǎn)時(shí)間超過timeout選項(xiàng)設(shè)置的值時(shí)情竹,客戶端將被關(guān)閉。
5)如果客戶端發(fā)送的命令請(qǐng)求個(gè)數(shù)大小超過了輸入緩沖區(qū)的限制大性群濉(默認(rèn)1GB)
6) 如果發(fā)送給客戶端的命令回復(fù)大小超過了輸出緩沖區(qū)的限制大小
2.3秦效、Lua腳本的偽客戶端
服務(wù)器會(huì)在初始化時(shí),創(chuàng)建負(fù)責(zé)執(zhí)行Lua腳本中包含的Redis命令的偽客戶端拱雏,并將其關(guān)聯(lián)到lua_client屬性中
lua_client偽客戶端在服務(wù)器運(yùn)行的整個(gè)生命周期中會(huì)一直存在棉安,只有服務(wù)器被關(guān)閉,這個(gè)客戶端才會(huì)被關(guān)閉
2.4铸抑、AOF文件的偽客戶端
服務(wù)器在載入AOF文件時(shí)贡耽,會(huì)創(chuàng)建用于執(zhí)行AOF文件包含的Redis命令的偽客戶端,在載入完成之后,關(guān)閉這個(gè)客戶端蒲赂。? ? ? ??