本文主要介紹Redis客戶端實現(xiàn),包括客戶端狀態(tài)快耿,與服務(wù)器的交互等內(nèi)容莫秆。
Redis是一對多服務(wù)器程序,通過使用由I/O多路復(fù)用技術(shù)實現(xiàn)的文件事件處理器极舔,Redis服務(wù)器使用單線程單進程的方式處理命令請求,并與多個客戶端進行網(wǎng)絡(luò)通信链瓦。
客戶端狀態(tài)由RedisClient結(jié)構(gòu)保存拆魏,服務(wù)器都為每個客戶端維持一個這樣的結(jié)構(gòu),其中包括的信息有:
· 客戶端的套接字描述符慈俯;
· 客戶端的名字渤刃;
· 指向客戶端正在使用的數(shù)據(jù)庫的指針,以及該數(shù)據(jù)庫的號碼贴膘;
· 客戶端當前要執(zhí)行的命令卖子、命令的參數(shù)、以及執(zhí)行命令實現(xiàn)函數(shù)的指針步鉴;
· 客戶端的輸入緩沖區(qū)與輸出緩沖區(qū)揪胃;
· 客戶端的復(fù)制狀態(tài)信息,以及進行復(fù)制所需的數(shù)據(jù)結(jié)構(gòu)氛琢;
· 客戶端執(zhí)行BRPOP
喊递,BLPOP
等列表阻塞命令時使用的數(shù)據(jù)結(jié)構(gòu);
· 客戶端的事務(wù)狀態(tài)阳似,以及執(zhí)行watch
命令時使用的數(shù)據(jù)結(jié)構(gòu)骚勘;
· 客戶端執(zhí)行發(fā)布與訂閱功能時使用的數(shù)據(jù)結(jié)構(gòu);
· 客戶端的身份驗證標志撮奏;
· 客戶端的創(chuàng)建時間俏讹,與服務(wù)器最后一次通信時間,以及客戶端的輸出緩沖區(qū)大小超出軟性限制的時間畜吊。
Redis服務(wù)器狀態(tài)結(jié)構(gòu)的clients屬性是一個鏈表泽疆,這個鏈表保存了所有與服務(wù)器連接的客戶端的狀態(tài)結(jié)構(gòu),對客戶端執(zhí)行批量操作玲献,或者查找某個指定的客戶端殉疼,都可以通過遍歷client鏈表完成梯浪。
struct redisServer {
// ...
list *clients;
// ...
};
下圖展示了這種結(jié)構(gòu):
I、客戶端屬性
1.1 套接字描述符
客戶端狀態(tài)的fd屬性記錄了客戶端正在使用的套接字描述符:
typedef struct redisClient {
//...
int fd;
//...
} redisClient;
偽客戶端的fd屬性為-1瓢娜,這包括載入AOF的偽客戶端以及用于執(zhí)行Lua腳本的偽客戶端挂洛。
普通客戶端的fd為大于-1 的整數(shù)。
執(zhí)行CLIENT list
命令可以列出目前所有連接到服務(wù)器的普通客戶端眠砾。
1.2 名字
可以使用CLIENT setname
命令為客戶端設(shè)置一個名字虏劲,名字保存在RedisClient的 那么屬性中。
1.3 標志
客戶端的標志屬性flags基類客戶端的角色褒颈,以及客戶端目前所處的狀態(tài)柒巫,存儲在redisClient的flags屬性中:
flags屬性可以是單個標志:flags = <flag>
, 也可以是二進制多個標志: flags = <flag1> | <flag2> | ...
具體標志所表示的含義可以參見Redis文檔。
1.4 輸入緩沖區(qū)
客戶端狀態(tài)的輸入緩沖區(qū)用于保存客戶端發(fā)送的命令請求:
typedef struct redisClient {
//...
sds querybuf;
//...
} redisClient;
1.5 命令與命令參數(shù)
在服務(wù)器將客戶端發(fā)送的命令請求保存到客戶端的querybuf屬性之后哈肖,服務(wù)器將對命令請求的內(nèi)容進行分析吻育,并將得出的命令參數(shù)以及命令參數(shù)的個數(shù)分別保存到客戶端狀態(tài)的argv屬性與argc屬性:
typedef struct redisClient {
//...
robj **argv;
int argc;
//...
} redisClient;
1.6 命令的實現(xiàn)函數(shù)
當服務(wù)器從協(xié)議內(nèi)容中分析并得出argv和argc屬性之后念秧,服務(wù)器會根據(jù)argv[0]的值淤井,在命令表中查找所對應(yīng)的命令實現(xiàn)函數(shù)。
下圖為一個命令表示例:
當程序在命令表中成功找到argv[0]所對應(yīng)的RedisCommand結(jié)構(gòu)時摊趾,會將客戶端狀態(tài)的cmd指針指向這個結(jié)構(gòu)币狠。
之后,服務(wù)器使用cmd屬相所指向的命令結(jié)構(gòu)砾层,以及argv和argc中保存的信息漩绵,完成客戶端命令。
1.7 輸出緩沖區(qū)
執(zhí)行命令所得到的回復(fù)會被保存到客戶端狀態(tài)的輸出緩沖區(qū)中肛炮,每個客戶端都有兩個輸出緩沖區(qū)止吐,一個緩沖區(qū)的大小是固定的,另一個是可變的:
· 固定大小的緩沖區(qū)用于保存長度較小的回復(fù)侨糟,如OK等碍扔,其以數(shù)組形式存儲;
· 可變大小的緩沖區(qū)用于保存那些長度比較大的回復(fù)秕重,如很多元素的集合不同,以鏈表形式存儲;
typedef struct redisClient {
//...
char buf[REDIS_REPLY_CHUNK_BYTES];
int bufpos;
list *reply;
//...
} redisClient;
buf是一個固定大小緩沖區(qū)的數(shù)組溶耘,bufpos屬性記錄了buf數(shù)組目前使用的字節(jié)數(shù)量二拐。
reply鏈表是可變緩沖區(qū)。
II凳兵、客戶端的創(chuàng)建與關(guān)閉
2.1 創(chuàng)建普通客戶端
前面已經(jīng)說過百新,服務(wù)器通過client鏈表來維護客戶端,每次創(chuàng)建的新客戶端都會添加到鏈表末尾庐扫。
2.2 關(guān)閉客戶端
服務(wù)器會因為多種原因關(guān)閉客戶端饭望,這里重點說明服務(wù)器為限制客戶端輸出緩沖區(qū)大小而采取的關(guān)閉客戶端措施澜倦。
· 硬性限制:如果緩沖區(qū)大小超過了硬性限制所設(shè)置的大小,則服務(wù)器立即關(guān)閉客戶端杰妓。
· 軟性限制:如果輸出緩沖區(qū)大小超過了軟性限制所設(shè)置的大小藻治,但還沒超過影響限制,服務(wù)器使用一個time屬性來記錄客戶端到達軟性限制的時間巷挥,如果緩沖區(qū)的大小一直超出軟性限制桩卵,并持續(xù)時間超過設(shè)定的時長,則會關(guān)閉客戶端倍宾;如果在未到達時長之前雏节,不再超出軟性限制,客戶端就不會關(guān)閉高职。
2.3 偽客戶端
服務(wù)器中包含兩個不需要socket的偽客戶端钩乍,分別執(zhí)行AOF load,與lua腳本
【參考】
[1] 《Redis設(shè)計與實現(xiàn)》
歡迎轉(zhuǎn)載怔锌,轉(zhuǎn)載請注明出處Redis之客戶端