溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何理解Redis 代碼庫源碼

發(fā)布時(shí)間:2021-11-17 15:13:18 來源:億速云 閱讀:128 作者:柒染 欄目:web開發(fā)

本篇文章給大家分享的是有關(guān)如何理解Redis 代碼庫源碼,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

Redis是一個(gè)用ANSI C  編寫的開源數(shù)據(jù)結(jié)構(gòu)服務(wù)器?!皵?shù)據(jù)結(jié)構(gòu)服務(wù)器”只是對(duì)靈巧的key-value存儲(chǔ)服務(wù)的另外一種稱謂。你不僅僅可以存儲(chǔ)簡單的字符串,還可以存儲(chǔ)包括 hash(或者map,甚至dicts),list,set,sorted set。我們在Top10 中 大量應(yīng)用了Redis,大部分為了根據(jù)用戶搜索的日期和酒店的空房情況和價(jià)格建立索引。我發(fā)現(xiàn)Redis的代碼非常容易讀懂,甚至是對(duì)于像我這樣的新手。 代碼寫的很整潔,并且代碼量相對(duì)較?。?.5萬行左右),大部分都是單線程的,依賴也很少。所有的依賴都跟源代碼放在一起了,這中做法讓編譯它變得非常簡 單:clone它的庫,然后輸入make即可。

我決定通過為它增加一條命令來深入代碼。而這簡單的事情可以讓我知道Redis怎么處理一條命令并調(diào)度響應(yīng)它。命令rand,接收一個(gè)整型值作為 max,并隨機(jī)返回0到max(不包含max)之間的一個(gè)整數(shù)。這不是使用鍵值存儲(chǔ)的思路,但是實(shí)現(xiàn)它將會(huì)很有啟發(fā)性。而我也肯定不會(huì)提交一個(gè)pull  request。

免責(zé)聲明:如我之前所說,我絕對(duì)不是一個(gè)C語言的專家,因此這里所有的代碼和其解釋都符合這個(gè)條款。而且,我鏈接了Redis的一個(gè)不穩(wěn)定分支,所以它是不穩(wěn)定的。如果你自己去獲取Redis源碼,用你喜歡的編輯器來查看時(shí),你將發(fā)現(xiàn)更多本文的不同,特別是如果你編譯并運(yùn)行時(shí)會(huì)發(fā)現(xiàn)不同。

命令表在src/redis.c文件的靠頂部的位置。它是一個(gè)數(shù)組,數(shù)組的元素類型是redisCommand結(jié)構(gòu)體。redisCommand是在src/redis.h中定義的。在redisCommandTable的上方有一塊比較詳細(xì)的注釋,對(duì)它的每一個(gè)field做了解釋。下面是get命令的定義:

{"get",getCommand,2,"r",0,NULL,1,1,1,0,0},

第一個(gè)field是命令的名字“get”。第二個(gè)field是一個(gè)函數(shù)指針,指向這個(gè)命令的具體實(shí)現(xiàn)(你可以查看實(shí)現(xiàn)細(xì)節(jié)t_string.c)。

第三個(gè)field是命令的參數(shù)數(shù)量限制(命令接收的參數(shù)個(gè)數(shù))。指定這個(gè),意味著在調(diào)用函數(shù)指針之前,查找和執(zhí)行命令的代碼可以做一個(gè)預(yù)先驗(yàn)證。這 種做法減少了在每個(gè)命令函數(shù)必須的錯(cuò)誤處理代碼。參數(shù)的個(gè)數(shù)算上了命令名字本身,所以它只接受兩個(gè)參數(shù):它自己的名字,key的名字(我們要獲取它的 值)。

第四個(gè)field,被設(shè)為”r”,用來指明這個(gè)命令是只讀的,不能修改這個(gè)key的value或狀態(tài)。有一大堆的字母標(biāo)志,你都可以用在這個(gè)位置。 而且在附近的注釋塊中,每個(gè)字母標(biāo)志都有詳細(xì)的解釋。緊跟這個(gè)field的field總是被設(shè)置為0,后面會(huì)用來計(jì)算。它只是第四個(gè)field的字符串包 含信息的位掩碼。

第六個(gè)field是NULL,因?yàn)樗挥性谀阋脧?fù)雜的邏輯去告訴Redis哪個(gè)參數(shù)才是真正的key的時(shí)候才需要。一個(gè)key指向一個(gè)存儲(chǔ)在 Redis中的值的引用,對(duì)應(yīng)簡單的參數(shù),例如我們的max參數(shù)。這種機(jī)制,允許Redis在調(diào)用命令的實(shí)現(xiàn)之前,提取key的值(并且校驗(yàn)key是否存 在)。如果這個(gè)field被設(shè)置了值,那么它將會(huì)是一個(gè)函數(shù)指針,指向的函數(shù)會(huì)返回一個(gè)參數(shù)索引的整型數(shù)組(db.c中 的zunionInterGetKeys是一個(gè)示例)。在get命令(其他大部分命令)的場景下,這個(gè)數(shù)組的信息傳達(dá)的信息跟后面三個(gè)field的一樣。 get命令只有一個(gè)參數(shù),而它就是key。因此,***個(gè)參數(shù)(key)在位置1上,***一個(gè)參數(shù)(也是key)在位置1上,從***個(gè)參數(shù)到***一個(gè)參數(shù)的 增量也是1(譯者注:源碼注釋是:intkeystep;/* The step between first and last key */)。

redisCommand的***兩個(gè)field是命令的度量項(xiàng),由Redis來設(shè)置,并且總是初始化為0。

在命令表的底部加上我們的命令:

{"rand",randCommand,2,"rRl",0,NULL,0,0,0,0,0}

命令的名字是“rand”,randCommand指向?qū)崿F(xiàn)的指針(還未實(shí)現(xiàn)),它接收2個(gè)參數(shù)(命令名字和max)。至于標(biāo)志,它是只讀的(r),返回隨機(jī)的,不確定的輸出(R),而且它可以在Redis還在加載數(shù)據(jù)的時(shí)候使用(l)。它沒有關(guān)鍵參數(shù)。

下一步是在src/redis.h中增加randCommand的函數(shù)原型。Redis命令的函數(shù)接收一個(gè)參數(shù),一個(gè)redisClient的結(jié)構(gòu)體,作為命令的參數(shù)同時(shí)也用來向?qū)嶋H的客戶端發(fā)送響應(yīng)。

void randCommand(redisClient *c);

這個(gè)原型應(yīng)該放在src/redis.h中與其他所有命令的原型一起。搜索下面的一行:

/* Commands prototypes */

這將幫你找到正確的位置。

我們在src/redis.c中加一個(gè)空實(shí)現(xiàn):

void randCommand(redisClient *c) {    }

我將它加在了infoCommand定義的旁邊?,F(xiàn)在,我們執(zhí)行make命令。

make

然后,啟動(dòng)我們剛剛編譯成共的Redis服務(wù)(如果你已經(jīng)有一個(gè)Redis服務(wù)在本地運(yùn)行,你應(yīng)該停掉它):

> src/redis-server

接著我們在另外的終端中運(yùn)行Redis客戶端,并試著運(yùn)行我們的命令:

>redis-cli

首先,我們試一試我們的異常處理:

redis 127.0.0.1:6379> rand  (error) ERR wrong number of arguments for 'rand' command

很好,參數(shù)數(shù)量限制檢查是正常的。這一次我們指定一個(gè)參數(shù):

redis 127.0.0.1:6379> rand 1

Redis卡住了。這正是我預(yù)期的,因?yàn)槲以趓andCommand函數(shù)中沒有任何響應(yīng)。將服務(wù)停掉,我們接著回去看代碼。

我們想返回一個(gè)整數(shù),因此我在代碼里翻找例子,***在src/t_zset.c中找到了zcardCommand。這個(gè)命令用addReplyLongLong來向客戶端返回一個(gè)64位(long long)的整數(shù)。我們也試一下:

void randCommand(redisClient *c) {      addReplyLongLong(c,3);  }

然后,我們在make一次,并測試命令:

redis 127.0.0.1:6379> rand 1  (integer) 3    redis 127.0.0.1:6379> rand 2  (integer) 3    redis 127.0.0.1:6379> rand 3  (integer) 3

好吧,結(jié)果不是太隨機(jī),但這只是個(gè)開始。我們從命令里獲取參數(shù)max,并返回一個(gè)由max限制的隨機(jī)數(shù):

void randCommand(redisClient *c) {      long max;        if (getLongFromObjectOrReply(c,c->argv[1],&max,NULL) != REDIS_OK)          return;        addReplyLongLong(c,random() % max);  }

盡管Redis在整個(gè)代碼庫中都用原始類型和C型字符串,但它同時(shí)也擁有自己的以更通用的方式存在的內(nèi)部對(duì)象系統(tǒng),用來表示字符串,長整 型和更復(fù)雜的類型。一個(gè)利用這種類型的例子就是:每個(gè)命令的參數(shù)。每一個(gè)命令的參數(shù)都作為一個(gè)Redis對(duì)象被存在redisClient實(shí)例c的 field,數(shù)組argv里。(譯注:在源碼src/redis.c里面redisClient是一個(gè)結(jié)構(gòu)體,argv是一個(gè)redisObject指針 的指針)。在src/t_string.c里面有一個(gè)從Redis對(duì)象獲取長整型的例子:getrangeCommand,它調(diào)用了src/object.c中的getLongFromObjectOrReply函數(shù)。

getLongFromObjectOrReply函數(shù)接收一個(gè)redisClient實(shí)例作參數(shù),并檢查它的第二個(gè)參數(shù)是否是一個(gè)長整型, 如果是則將第二個(gè)參數(shù)的指針賦給第三個(gè)參數(shù)(這個(gè)參數(shù)是一個(gè)指針類型),并且返回REDIS_OK。如果第二個(gè)參數(shù)不是長整型(或溢出了),函數(shù)返回 REDIS_ERR。這個(gè)方法的美麗之處在于:如果我們從我們的randCommand函數(shù)得到的返回值是REDIS_ERR,所有必須的錯(cuò)誤響應(yīng)已經(jīng)被 發(fā)送給客戶端了。我們再試一下我們的命令:

redis 127.0.0.1:6379> rand 10  (integer) 9  redis 127.0.0.1:6379> rand notanumber  (error) ERR value is not an integer or out of range  redis 127.0.0.1:6379> rand 10  (integer) 3  redis 127.0.0.1:6379> rand 10  (integer) 1  redis 127.0.0.1:6379> rand 100  (integer) 43  redis 127.0.0.1:6379> rand 100  (integer) 55  redis 127.0.0.1:6379> rand 100  (integer) 86

看起來不錯(cuò)!rand看起來是一個(gè)沒有多少意義的命令,但是從實(shí)現(xiàn)它的過程中學(xué)到很多關(guān)于Redis的東西,我希望你跟著做下來也同樣學(xué)到很多。請?jiān)谠u(píng)論 里告訴我這篇文章里是否明顯的錯(cuò)誤。我也很高興知道這篇文章對(duì)你很有用或者你很喜歡它。我考慮寫一些類似的東西,關(guān)于Redis或者其他的開源的代碼庫。

以上就是如何理解Redis 代碼庫源碼,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過這篇文章學(xué)到更多知識(shí)。更多詳情敬請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI