你知道 Redis 服務(wù)器接收到一條命令是如何執(zhí)行的嗎?
本文轉(zhuǎn)載自微信公眾號(hào)「Java極客技術(shù)」,作者鴨血粉絲。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java極客技術(shù)公眾號(hào)。
Hello 大家好,我是阿粉,Redis 作為工作中不可缺少的緩存組件,相信很多小伙伴都會(huì)使用到,我們?nèi)粘J褂玫臅r(shí)候都是通過(guò)代碼或者客戶端去鏈接 Redis 服務(wù)器來(lái)操作數(shù)據(jù)的。那么一條簡(jiǎn)單的set name ziyou 命令是如何執(zhí)行的,中間都經(jīng)歷了哪些過(guò)程想必很少會(huì)有人去了解。今天阿粉就帶大家看一下一條簡(jiǎn)單的set name ziyou 命令是如何執(zhí)行的。
我們可以看到在執(zhí)行set name ziyou 這個(gè)命令過(guò)后,先顯示一個(gè)OK 在終端里面。下面我們看下這整個(gè)過(guò)程都經(jīng)歷了哪些步驟。
命令的整個(gè)執(zhí)行分為下面幾個(gè)步驟,我們先看流程,在仔細(xì)分析:
- 客戶端發(fā)送命令請(qǐng)求;
- 服務(wù)端讀取命令請(qǐng)求;
- 命令執(zhí)行器進(jìn)行操作
- 命令執(zhí)行器查找命令實(shí)現(xiàn)函數(shù);
- 命令執(zhí)行器執(zhí)行預(yù)備操作;
- 命令執(zhí)行器調(diào)用命令的實(shí)現(xiàn)函數(shù);
- 命令執(zhí)行器執(zhí)行后續(xù)工作;
- 服務(wù)端將命令回復(fù)發(fā)送給客戶端;
- 客戶端接收并打印命令回復(fù)內(nèi)容;
客戶端發(fā)送命令請(qǐng)求
首先當(dāng)客戶端和服務(wù)端建立好了鏈接過(guò)后,當(dāng)我們輸入命令 set name ziyou 命令請(qǐng)求的時(shí)候,客戶端會(huì)將這個(gè)命令進(jìn)行協(xié)議轉(zhuǎn)換,然后通過(guò)連接將轉(zhuǎn)換后的協(xié)議發(fā)送到服務(wù)端。
比如當(dāng)我們輸入命令set name ziyou 的時(shí)候,客戶端會(huì)將這個(gè)原始命令轉(zhuǎn)換成*3\r\n$3\r\nset\r\n$4\r\nname\r\n$5\r\nziyou,這個(gè)協(xié)議大家應(yīng)該比較眼熟,就是 Redis 管道的文件格式。簡(jiǎn)單解釋下這個(gè)協(xié)議的意思,前面的*3 表示這個(gè)命令總共有三個(gè)參數(shù),其中的$3,$4,$5 表示相應(yīng)參數(shù)的長(zhǎng)度。
服務(wù)端讀取命令請(qǐng)求
當(dāng)服務(wù)端收到該客戶端的數(shù)據(jù)時(shí),就會(huì)調(diào)用命令請(qǐng)求處理器來(lái)處理對(duì)應(yīng)的消息。這塊主要涉及到三個(gè)操作,第一個(gè)是保存命令,也就是會(huì)將命名的請(qǐng)求信息讀取出來(lái)保存到對(duì)應(yīng)客戶端的輸入緩沖區(qū)里面;保存完了過(guò)后會(huì)對(duì)輸入緩沖區(qū)里面的內(nèi)容進(jìn)行解析,也就是對(duì)上面轉(zhuǎn)換后的協(xié)議進(jìn)行解析,解析出要執(zhí)行的命令和對(duì)應(yīng)的參數(shù),將參數(shù)內(nèi)容和參數(shù)個(gè)數(shù)保存到客戶端的對(duì)應(yīng)參數(shù)里面;第三步是調(diào)用命令執(zhí)行器來(lái)執(zhí)行命令。執(zhí)行的命令和參數(shù)保存在RedisClient 結(jié)構(gòu)的 argv 參數(shù)中,如下圖所示,命令分析完成后,第三步才能更好的進(jìn)行執(zhí)行操作:
命令執(zhí)行器
命令執(zhí)行器查找實(shí)現(xiàn)函數(shù)
思考一個(gè)問題,我們這里 argv[0] 參數(shù)中的命令的是進(jìn)行set 操作,在這里是個(gè) set 字符串,那么 Redis 服務(wù)器是如何進(jìn)行執(zhí)行的呢?我們可以想到的是需要根據(jù)這個(gè)字符串找到對(duì)應(yīng)的函數(shù)來(lái)進(jìn)行操作,Redis 在內(nèi)部有個(gè)的命令表,是一個(gè)字典結(jié)果,key就是對(duì)應(yīng)的命令名字,字典的值就是一個(gè)個(gè) RedisCommand 結(jié)構(gòu),記錄了命令的實(shí)現(xiàn)信息。
結(jié)構(gòu)如下,簡(jiǎn)單來(lái)說(shuō)就是通過(guò) argv[0] 中的命令名稱找到命令表中對(duì)應(yīng)的redisCommand 結(jié)構(gòu),然后根據(jù) proc 指針找到對(duì)應(yīng)的執(zhí)行命令。這里說(shuō)明一下,命令名稱的大小寫沒有任何影響,我們?cè)谳斎氲臅r(shí)候不用關(guān)心命令名稱的大小寫問題。
命令執(zhí)行器執(zhí)行預(yù)備操作
在 Redis 服務(wù)器執(zhí)行相關(guān)命令之前,為了保證命令能夠正確的執(zhí)行,還需要進(jìn)行相關(guān)的預(yù)備處理,部分預(yù)操作如下:
- 檢查命令的參數(shù)和輸入的參數(shù)個(gè)數(shù)是否一致,不一致則直接返回錯(cuò)誤;
- 檢查客戶端是否通過(guò)身份驗(yàn)證,未通過(guò)身份驗(yàn)證則只能執(zhí)行 AUTH 命令進(jìn)行身份驗(yàn)證;
- 檢查服務(wù)器的內(nèi)容使用情況,為了保證命令執(zhí)行成功,可能會(huì)需要進(jìn)行內(nèi)容回收;
除了上面的功能之外還有很多需要預(yù)備執(zhí)行的動(dòng)作,而且根據(jù)服務(wù)器部署的情況不一樣,單機(jī)還是集群需要執(zhí)行的操作還有不同。只有當(dāng)所有的 預(yù)備操作都執(zhí)行成功過(guò)后,才會(huì)真正的執(zhí)行用戶的命令。
由此可見 Redis 的性能是真正的高效,在有這么做操作流程的情況下還能保住命令執(zhí)行的如此快速,不得不說(shuō)真的很優(yōu)秀。
命令執(zhí)行器調(diào)用命令的實(shí)現(xiàn)函數(shù)
當(dāng)前面的預(yù)備操作都完成過(guò)后,命令執(zhí)行器就會(huì)調(diào)用對(duì)應(yīng)的實(shí)現(xiàn)函數(shù),在我們這里的例子就是調(diào)用 setCommand(redisClient *c) 函數(shù)進(jìn)行數(shù)據(jù)寫入操作,具體的 key 值和 value 值在 redisClient 結(jié)構(gòu)中已經(jīng)保存了,所以只要傳遞一個(gè)指針進(jìn)去就可以了。setCommand() 命令執(zhí)行后會(huì)返回一個(gè)OK\r\n ,這個(gè)返回會(huì)被保存到客戶端的輸出緩沖區(qū)當(dāng)中,輸出緩沖區(qū)的內(nèi)容后續(xù)會(huì)被返回到客戶端,給用戶展示出來(lái),如前面的圖片顯示的內(nèi)容。
命令執(zhí)行器執(zhí)行后續(xù)工作
當(dāng)命令執(zhí)行器調(diào)用具體的實(shí)現(xiàn)函數(shù)過(guò)后,服務(wù)器還會(huì)有相應(yīng)的一些操作要做,比如如果開啟了慢日志功能,會(huì)檢查是否要寫入慢日志;如果開啟了 AOF 則需要將剛剛執(zhí)行的命令寫入 AOF 的緩沖區(qū)中;以及如果有服務(wù)器備份或者監(jiān)聽的時(shí)候,會(huì)把剛剛執(zhí)行的命令廣播過(guò)去。
服務(wù)端將命令回復(fù)發(fā)送給客戶端
實(shí)現(xiàn)函數(shù)執(zhí)行完過(guò)后會(huì)將執(zhí)行結(jié)果保存到客戶端的輸出緩沖區(qū)中,此時(shí)服務(wù)器的命令回復(fù)處理器會(huì)將緩沖區(qū)中的命令回復(fù)發(fā)送給客戶端。命令回復(fù)處理器發(fā)送完數(shù)據(jù)過(guò)后會(huì)將客戶端的輸出緩沖區(qū)清理,方便后續(xù)的命令存入數(shù)據(jù),同樣回復(fù)的數(shù)據(jù)也是經(jīng)過(guò)協(xié)議轉(zhuǎn)換的。
客戶端接收并打印命令回復(fù)內(nèi)容
客戶端收到回復(fù)數(shù)據(jù)過(guò)后就數(shù)據(jù)轉(zhuǎn)換成可讀的形式,輸出到控制臺(tái)。這樣就得到了我們第一張圖片的結(jié)果。
總結(jié)
通過(guò)上面所有的過(guò)程,我們可以看到,就是一個(gè)簡(jiǎn)單的set name ziyou 這樣的語(yǔ)句,整個(gè)執(zhí)行的過(guò)程也還是很復(fù)雜的,Redis 服務(wù)器在設(shè)計(jì)的時(shí)候要考慮很多東西,安全,性能等等方面。
引用
《Redis 設(shè)計(jì)與實(shí)現(xiàn)第二版》