成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

跟著大彬讀源碼 - Redis 3 - 服務(wù)器如何響應(yīng)客戶端請(qǐng)求?(下)

roadtogeek / 1483人閱讀

摘要:讀取命令請(qǐng)求當(dāng)客戶端與服務(wù)器之間的套接字因客戶端的寫入變得可讀時(shí),服務(wù)器將調(diào)用命令請(qǐng)求處理器執(zhí)行以下操作讀取套接字中的命令請(qǐng)求,并將其保存到客戶端狀態(tài)的輸入緩沖區(qū)。

繼續(xù)我們上一節(jié)的討論。服務(wù)器啟動(dòng)了,客戶端也發(fā)送命令了。接下來,就要到服務(wù)器“表演”的時(shí)刻了。

1 服務(wù)器處理

服務(wù)器讀取到命令請(qǐng)求后,會(huì)進(jìn)行一系列的處理。

1.1 讀取命令請(qǐng)求

當(dāng)客戶端與服務(wù)器之間的套接字因客戶端的寫入變得可讀時(shí),服務(wù)器將調(diào)用命令請(qǐng)求處理器執(zhí)行以下操作:

讀取套接字中的命令請(qǐng)求,并將其保存到客戶端狀態(tài)的輸入緩沖區(qū)。

對(duì)輸入緩沖區(qū)的命令請(qǐng)求進(jìn)行分析,提取出命令請(qǐng)求中包含的命令參數(shù)及參數(shù)個(gè)數(shù),然后分別將參數(shù)和參數(shù)個(gè)數(shù)保存到客戶端狀態(tài)的 argv 屬性和 argc 屬性里。

調(diào)用命令執(zhí)行器,執(zhí)行客戶端指定的命令。

上面的 SET 命令保存到客戶端狀態(tài)的輸入緩存區(qū)之后,客戶端狀態(tài)如圖 4。

之后,分析程序?qū)?duì)輸入緩沖區(qū)中的協(xié)議進(jìn)行分析,并將得出的結(jié)果保存的客戶端的 argv 和 argc 屬性中,如圖 5 所示:

之后,服務(wù)器將通過調(diào)用命令執(zhí)行器來完成執(zhí)行命令的余下步驟。

1.2 查找命令實(shí)現(xiàn)

命令執(zhí)行器要做的第一件事就是根據(jù) argv[0] 參數(shù),在命令表(commandtable)中查找參數(shù)所指定的命令,并將找到的命令保存到 cmd 屬性中。

命令表是一個(gè)字典,字典的鍵是一個(gè)個(gè)命令名稱,比如 "SET"、"GET" 等。而字典的值則是一個(gè)個(gè) redisCommand 結(jié)構(gòu),每個(gè) redisCommand 結(jié)構(gòu)記錄了 Redis 命令的實(shí)現(xiàn)信息。源碼如下:

# server.h/redisCommand
struct redisCommand {
    char *name;   // 命令名稱。如 "SET"
    redisCommandProc *proc; // 對(duì)應(yīng)函數(shù)指針,指向命令的實(shí)現(xiàn)函數(shù)。比如 SET 對(duì)應(yīng)的 setCommand 函數(shù)
    int arity;    // 命令參數(shù)的格個(gè)數(shù)。用來檢查命令請(qǐng)求的格式是否合法。
                        // 要注意的命令的名稱也是一個(gè)參數(shù)。像我們上面的 SET KEY VALUE 命令,實(shí)際上有三個(gè)參數(shù)。
    char *sflags; // 字符串形式的標(biāo)識(shí)值。記錄了命令的屬性。
    int flags;    // 對(duì) sflags 標(biāo)識(shí)分析得出的二進(jìn)制標(biāo)識(shí),由程序自動(dòng)生成。檢查命令時(shí),實(shí)際上使用的是此字段
    redisGetKeysProc *getkeys_proc; // 指針函數(shù),通過此方法來指定 key 的位置。
    int firstkey; // 第一個(gè) key 的位置
    int lastkey;  // 最后一個(gè) key 的位置
    int keystep;  // key 之間的間距
    long long microseconds, calls; // 命令的總調(diào)用時(shí)間及調(diào)用次數(shù)
};

另外,對(duì)于 sflags 屬性,可使用的標(biāo)識(shí)值及含義如下表:

標(biāo)識(shí) 意義 帶有此標(biāo)識(shí)的命令
w 這是一個(gè)寫入命令,可能會(huì)修改數(shù)據(jù)庫 SET、RPUSH、DEL 等
r 這是一個(gè)只讀命令,不會(huì)修改數(shù)據(jù)庫 GET、STRLEN 等
m 此命令可能會(huì)占用大量?jī)?nèi)存,執(zhí)行器需先檢查內(nèi)存使用情況,如果內(nèi)存緊缺就禁止執(zhí)行此命令 SET、APPEND、RPUSH、SADD 等
a 這是一個(gè)管理命令 SAVE、BGSAVE 等
p 這是一個(gè)發(fā)布與訂閱功能的命令 PUBLISH、SUBSRIBE 等
s 這個(gè)命令不可以在 lua 腳步中使用 BPOP、BLPOP 等
R 這是一個(gè)隨機(jī)命令。對(duì)于相同的數(shù)據(jù)集和相同的參數(shù),返回結(jié)果可能不同 SPOP、SRANDMEMBER 等
S 當(dāng)在 lua 腳步中使用此命令時(shí),對(duì)返回結(jié)果進(jìn)行排序,使得結(jié)果有序 SINTER、SUNION 等
l 這個(gè)命令可以在服務(wù)器載入數(shù)據(jù)的過程中使用 INFO、PUBLISH 等
t 這個(gè)命令允許在從庫有過期數(shù)據(jù)時(shí)使用 SLAVEOF、PING 等
M 這個(gè)命令在監(jiān)視模式下,不會(huì)被自動(dòng)傳播 EXEC
k 集群模式下,如果對(duì)應(yīng)槽點(diǎn)標(biāo)記位“導(dǎo)入”,則接受此命令 restore-asking
F 這個(gè)命令在程序執(zhí)行時(shí)應(yīng)該立刻執(zhí)行 SETNX、GET 等

命令表結(jié)構(gòu)如圖 6:

對(duì)于我們上面的 SET KEY VALUE 命令,當(dāng)程序以圖 5 中的 argv[0] 作為輸入,在命令表中進(jìn)行查找時(shí),命令表返回 "set" 鍵對(duì)于的 redisCommand 結(jié)構(gòu),客戶端狀態(tài)的 cmd 指針會(huì)指向這個(gè) redisCommand 結(jié)構(gòu)。如圖 7 所示:

要注意的是,對(duì)于 Redis 而言,命令名字的大小寫不影響命令表的查找結(jié)果,也就是命令名稱不區(qū)分大小寫。執(zhí)行 SET 和 set、Set 將獲得相同結(jié)果。

1.3 執(zhí)行預(yù)備操作

到目前為止,服務(wù)器已經(jīng)將執(zhí)行命令所需要的命令實(shí)現(xiàn)函數(shù)(客戶端 cmd 屬性)、參數(shù)(客戶端 argv 屬性)、參數(shù)個(gè)數(shù)(客戶端 argc 屬性)都初始化完畢。但在真正執(zhí)行命令之前,程序還會(huì)進(jìn)行一些預(yù)備操作,保證命令可以正確、順利的被執(zhí)行。預(yù)備操作包括:

檢查客戶端的 cmd 指針是否指向 NULL,如果是的話,說明用戶輸入的命令名稱沒有對(duì)應(yīng)的函數(shù),服務(wù)器將不再執(zhí)行后續(xù)操作,并向客戶端返回一個(gè)錯(cuò)誤。

根據(jù)客戶端 cmd 屬性指向的 redisCommand 結(jié)果的 arity 屬性,檢查命令請(qǐng)求所給定的參數(shù)個(gè)數(shù)是否正確。

檢查客戶端是否已經(jīng)通過了身份驗(yàn)證。未通過身份驗(yàn)證的客戶端只能執(zhí)行 AUTH 命令。否則,將會(huì)向客戶端返回一個(gè)錯(cuò)誤。

如果服務(wù)器打開了 maxmemory 功能,在執(zhí)行命令之前,會(huì)先檢查服務(wù)器的內(nèi)存占用情況,并在有需要時(shí)進(jìn)行內(nèi)存回收,從而使得接下來的命令可以順利執(zhí)行。如果內(nèi)存回收失敗,將不再執(zhí)行后續(xù)步驟,向客戶端返回一個(gè)錯(cuò)誤。

如果服務(wù)器上一次執(zhí)行 BGSAVE 命令時(shí)出錯(cuò),并且服務(wù)器打開了 stop-writes-on-bgsave-error 功能,而將要執(zhí)行的命令是一個(gè)寫命令,那么服務(wù)器將拒絕執(zhí)行這個(gè)鞋命令,并向客戶端返回一個(gè)錯(cuò)誤。

如果客戶端正在用 SUBSCRIBEPSUBSCRIBE 命令訂閱頻道或模式,那么服務(wù)器只會(huì)執(zhí)行客戶端發(fā)來的 SUBSCRIBEPSUBSCRIBE、UNSUBSCRIBE、PUNSUBSCRIBE 四個(gè)命令,其它命令都會(huì)被拒絕。

如果服務(wù)器正在進(jìn)行數(shù)據(jù)載入,那么客戶端發(fā)送是命令必須帶有 l 標(biāo)識(shí)才會(huì)被服務(wù)器執(zhí)行。

如果客戶端正在執(zhí)行事務(wù),那么服務(wù)器只會(huì)執(zhí)行 EXEC、DISCARD、MULTI、WATCH 四個(gè)命令,其他命令都會(huì)被放進(jìn)事務(wù)隊(duì)列中。

如果服務(wù)器打開了監(jiān)視器功能,那么服務(wù)器會(huì)將要執(zhí)行的命令和參數(shù)等信息發(fā)送給監(jiān)視器。

當(dāng)完成了以上預(yù)備操作之后,服務(wù)器就開始真正的執(zhí)行命令了。

要注意的是,上面列出的預(yù)備操作只是服務(wù)器在單機(jī)模式下的檢查操作。如果在復(fù)制或者集群模式下,預(yù)備操作還會(huì)更多。

1.4 調(diào)用命令的實(shí)現(xiàn)函數(shù)

在前面的操作中 ,服務(wù)器已經(jīng)將要執(zhí)行的命令實(shí)現(xiàn)、參數(shù)、參數(shù)個(gè)數(shù)保存在客戶端結(jié)構(gòu)中。

對(duì)于我們上面的 SET KEY VALUE 命令,圖 8 包含了命令實(shí)現(xiàn)、參數(shù)和參數(shù)個(gè)數(shù)結(jié)構(gòu):

當(dāng)服務(wù)器決定要執(zhí)行命令時(shí),只要執(zhí)行以下語句即可:

// client 是指向客戶端狀態(tài)的指針。server.c/call()
client->cmd->proc(client);

上面的執(zhí)行語句實(shí)際上就是調(diào)用 setCommand 函數(shù)(t_string.c)。

被調(diào)用的命令實(shí)現(xiàn)函數(shù)會(huì)執(zhí)行指定的操作,并產(chǎn)生相應(yīng)的命令回復(fù),這些回復(fù)會(huì)被保存在客戶端狀態(tài)的輸出緩沖區(qū)中(bug 屬性 和 reply 屬性),之后實(shí)現(xiàn)函數(shù)會(huì)為客戶端的套接字關(guān)聯(lián)命令回復(fù)處理器,由命令回復(fù)處理器返回給客戶端。

回到我們的示例,setCommand(client) 將產(chǎn)生一個(gè) "+OKrn" 回復(fù),這個(gè)回復(fù)被保存在客戶端的 buf 屬性中。如圖 9 所示:

1.5 執(zhí)行后續(xù)工作

實(shí)現(xiàn)函數(shù)執(zhí)行完后,服務(wù)器還會(huì)執(zhí)行一些后續(xù)工作,主要包括:

如果服務(wù)器開啟了 slow-log 功能,那么慢查詢?nèi)罩灸K將會(huì)檢查是否需要將剛執(zhí)行的命令添加到慢查詢?nèi)罩尽?/p>

更新 redisCommand 結(jié)構(gòu)的 milliseconds 和 calls 屬性。

如果服務(wù)器開啟了 AOF 持久化功能,那么 AOF 持久化模塊會(huì)將剛剛執(zhí)行的命令請(qǐng)求寫入到 AOF 緩沖區(qū)中。

如果有其它服務(wù)器正在復(fù)制當(dāng)前這個(gè)服務(wù)器,那么服務(wù)器將會(huì)把剛剛執(zhí)行的命令傳播給所有從服務(wù)器。

以上后續(xù)操作執(zhí)行完畢后,一條執(zhí)行命令也就執(zhí)行完成了。服務(wù)器可以繼續(xù)處理后續(xù)的命令。

1.6 將命令回復(fù)發(fā)送給客戶端

上面過程中,命令實(shí)現(xiàn)函數(shù)會(huì)將命令回復(fù)保存到客戶端的輸出緩沖區(qū)中,并為客戶端的套接字關(guān)聯(lián)命令回復(fù)處理器。當(dāng)客戶端套接字變?yōu)榭蓪憼顟B(tài)時(shí),服務(wù)器就會(huì)執(zhí)行命令回復(fù)處理器,將命令回復(fù)發(fā)送給客戶端。

當(dāng)命令回復(fù)發(fā)送完畢后,回復(fù)處理器會(huì)情況客戶端的輸出緩沖區(qū),為處理下一個(gè)命令請(qǐng)求做好準(zhǔn)備。

以圖 9 所示的客戶端狀態(tài)為例,當(dāng)客戶端的套接字變?yōu)榭蓪憼顟B(tài)時(shí),命令回復(fù)處理器會(huì)將協(xié)議格式的命令回復(fù) "+OKrn" 發(fā)送給客戶端。

1.7 源碼解讀

命令處理請(qǐng)求,函數(shù)調(diào)用堆棧信息如圖 3-7-1:

命令回復(fù),函數(shù)調(diào)用堆棧信息如圖 3-7-2:

2 客戶端接收并打印回復(fù)

客戶端接收到命令回復(fù)之后,會(huì)將回復(fù)轉(zhuǎn)換成我們可讀的格式,并打印在屏幕上(對(duì)于 redis-cli 客戶端),如圖 10 所示。

至此,我們走完了從發(fā)起一個(gè)命令請(qǐng)求,到收到回復(fù)的所有過程。對(duì)于我們最開始提的問題,服務(wù)器如何響應(yīng)客戶端請(qǐng)求,你有答案了嗎?

總結(jié)

服務(wù)器通過 networking.c/readQueryFromClient() 讀取和執(zhí)行對(duì)應(yīng)命令。

服務(wù)器通過 networking.c/writeToClient() 將命令回復(fù)發(fā)送給客戶端。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/62115.html

相關(guān)文章

  • 跟著彬讀源碼 - Redis 2 - 務(wù)器如何響應(yīng)戶端請(qǐng)求?(上)

    摘要:現(xiàn)在客戶端和服務(wù)器都準(zhǔn)備好了,那么客戶端和服務(wù)器如何建立連接服務(wù)器又是如何響應(yīng)客戶端的請(qǐng)求呢連接服務(wù)器客戶端和服務(wù)器進(jìn)行通訊,首先應(yīng)該就是建立連接。以上是客戶端發(fā)送命令給服務(wù)器的過程,在下一節(jié)中,我們?cè)賮碚J(rèn)識(shí)服務(wù)器是如何響應(yīng)客戶端請(qǐng)的。 上次我們通過問題啟動(dòng)服務(wù)器,程序都干了什么?,跟著源碼,深入了解了 Redis 服務(wù)器的啟動(dòng)過程。 既然啟動(dòng)了 Redis 服務(wù)器,那我們就要連上 R...

    Anonymous1 評(píng)論0 收藏0
  • 跟著彬讀源碼 - Redis 1 - 啟動(dòng)服務(wù),程序都干了什么?

    摘要:此時(shí)服務(wù)器處于休眠狀態(tài),并使用進(jìn)行事件輪詢,等待監(jiān)聽事件的發(fā)生。繼續(xù)執(zhí)行被調(diào)試程序,直至下一個(gè)斷點(diǎn)或程序結(jié)束縮寫。服務(wù)啟動(dòng)包括初始化基礎(chǔ)配置數(shù)據(jù)結(jié)構(gòu)對(duì)外提供服務(wù)的準(zhǔn)備工作還原數(shù)據(jù)庫執(zhí)行事件循環(huán)等。 一直很羨慕那些能讀 Redis 源碼的童鞋,也一直想自己解讀一遍,但迫于 C 大魔王的壓力,解讀日期遙遙無期。 相信很多小伙伴應(yīng)該也都對(duì)或曾對(duì)源碼感興趣,但一來覺得自己不會(huì) C 語言,二來也...

    sewerganger 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<