Redispy 源碼學(xué)習(xí)(二) --- RESP協(xié)議簡(jiǎn)介

網(wǎng)絡(luò)通信離不開各種各樣的協(xié)議,著名的tcp历谍,http等協(xié)議構(gòu)建了我們常見的web應(yīng)用现拒。http協(xié)議是基于tcp的應(yīng)用層協(xié)議。同樣的望侈,redis的協(xié)議也是基于tcp的應(yīng)用層協(xié)議印蔬,即RESP(REdis Serialization Protocol)。

RESP設(shè)計(jì)巧妙脱衙,它的追求在于下面三個(gè)方面:

  • 易于實(shí)現(xiàn)
  • 解析高效
  • 易于人讀

協(xié)議基礎(chǔ)

RESP協(xié)議規(guī)定侥猬,客戶端通過tcp網(wǎng)絡(luò)連接到redis服務(wù)器。通信過程類似一問一答的方式捐韩,與HTTP類似退唠,客戶端發(fā)送命令請(qǐng)求到服務(wù)器,服務(wù)器執(zhí)行時(shí)候返回結(jié)果荤胁。

客戶端和服務(wù)器發(fā)送的命令或數(shù)據(jù)一律以 \r\n (CRLF)結(jié)尾瞧预。正式因?yàn)镃RLF的存在,讓redis的解析十分方便和可讀寨蹋。

請(qǐng)求協(xié)議

我們知道松蒜,redis數(shù)據(jù)庫操作的命令無非就三種,第一中是不帶參數(shù)的命令已旧,例如PING秸苗, FLUSHDB;其次是帶有參數(shù)的讀取命令运褪,例如GET key惊楼,LLEN queue;還有帶有參數(shù)并設(shè)置值的寫操作秸讹,例如 SET hello world檀咙,CONFIG SET TIMEOUT 30

命令和參數(shù)之間璃诀,通常都是用空格隔開弧可。RESP協(xié)議規(guī)定,這樣的請(qǐng)求中劣欢,命令和各參數(shù)之間必須使用CRLF分割棕诵,同時(shí)還必須提供命令頭標(biāo)簽,即token凿将。字節(jié)串的第一個(gè)字符到CRLF的內(nèi)容即為頭標(biāo)簽校套。

參數(shù)中的字符串和數(shù)字

字串

一個(gè)redis數(shù)據(jù)庫操作包含命令+參數(shù)。當(dāng)然有的命令參數(shù)可以省略牧抵。無論編碼命令還是參數(shù)笛匙,其編碼方式都是一樣的侨把。

命令中,無論請(qǐng)求和返回的結(jié)果妹孙,無怪乎就只有字符串和數(shù)字秋柄。編碼字符串的格式如下:

$ + string_length + string + CRLF,即以$符號(hào)開頭蠢正,然后跟字節(jié)串的長(zhǎng)度华匾,然后跟著一個(gè)CRLF,再然后就是字節(jié)串本身机隙,最后以一個(gè)CRLF結(jié)尾蜘拉。

例如 hello編碼成 $5\r\nhello\r\n21.7編碼成$3\r\n21.7\r\n有鹿。其中$5\r\n$3\r\n都是Token旭旭。

數(shù)字

數(shù)字類型和字符串類似,不同的在于數(shù)字使用:開頭葱跋,同時(shí)不需要說明數(shù)字的長(zhǎng)度持寄。

: + number + \r\n,即:開頭娱俺,然后跟著數(shù)字稍味,最后以CRLF結(jié)尾即可。

注意荠卷,redis中絕大多數(shù)參數(shù)都是字串模庐,只有返回響應(yīng)的時(shí)候會(huì)處理數(shù)字類型,例如incr操作油宜。其他情況下掂碱,請(qǐng)求的參數(shù),還是返回響
應(yīng)中的數(shù)字慎冤,其實(shí)都是數(shù)字字符串疼燥。例如下面是一個(gè)集合,返回元素的時(shí)候蚁堤,里面的元素都是字串

127.0.0.1:6379> sadd set hello
(integer) 1
127.0.0.1:6379> sadd set 世界
(integer) 1
127.0.0.1:6379> sadd set 1
(integer) 1
127.0.0.1:6379> sadd set 21.7
(integer) 1
127.0.0.1:6379> smembers set
1) "1"
2) "21.7"
3) "\xe4\xb8\x96\xe7\x95\x8c"
4) "hello"

sadd的set的參數(shù)中醉者,1,和21.7都是字串類型披诗,返回自然是字串撬即,而返回表示sadd成功的則是數(shù)字類型。所有請(qǐng)求的命令都是字串類型藤巢,哪怕寫的是數(shù)字字面量搞莺,redis最終還是當(dāng)成字串處理息罗。

編碼命令

上面我們了解單獨(dú)的命令和參數(shù)的編碼方式掂咒。下面介紹命令+參數(shù)的組合編碼方式。

組合編碼也很簡(jiǎn)單,無非就是將編碼的命令(字符串)拼接起來绍刮,同時(shí)再最開始追加一個(gè)* + 組合命令或參數(shù)的個(gè)數(shù)的方式:

*<參數(shù)數(shù)量> CR LF
$<參數(shù) 1 的字節(jié)數(shù)量> CR LF
<參數(shù) 1 的數(shù)據(jù)> CR LF
...
$<參數(shù) N 的字節(jié)數(shù)量> CR LF
<參數(shù) N 的數(shù)據(jù)> CR LF

例如 PING 編碼字符串的方式為$4\r\nPING\r\n温圆,組合的編碼為*1\r\n$4\r\nPING\r\n。即

*1
$4
PING

GET hello編碼為*2\r\n$3\r\nGET\r\n$5\r\nhello\r\n

*2
$3
GET
$5
hello

GEThello兩個(gè)字符串分別編碼孩革,然后組合起來岁歉,編碼了兩個(gè)參數(shù),因此*后面跟著是2膝蜈。

再看一個(gè)例子锅移,SET 中國 21.7的將編碼成*3\r\n$3\r\nSET\r\n$6\r\n\xe4\xb8\xad\xe5\x9b\xbd\r\n$4\r\n21.7\r\n

*3
$3
SET
$6
\xe4\xb8\xad\xe5\x9b\xbd
$4
21.7

漢字中國的按照utf-8編碼的方式編碼成bytes為*\xe4\xb8\xad\xe5\x9b\xbd,一個(gè)漢字三個(gè)字節(jié)饱搏,因此中國的長(zhǎng)度是6非剃。

python3的字串原生支持unicode,計(jì)算機(jī)操作字串的時(shí)候是unicode推沸,當(dāng)需要網(wǎng)絡(luò)傳輸和寫入文件的時(shí)候备绽,都必須把unicode的字串編碼成bytes結(jié)構(gòu)。同理鬓催,當(dāng)從網(wǎng)絡(luò)或者文件中讀取數(shù)據(jù)的時(shí)候肺素,也需要解碼成unicode。目前國際通用的編碼方式以utf-8為主宇驾。

值得注意的是倍靡,將命令參數(shù)組合的編碼方式,不僅是請(qǐng)求的時(shí)候如此课舍,在redis的響應(yīng)回復(fù)中菌瘫,其中的多批量回復(fù)(multi-bulk reply)也是采用了同樣的編碼方式返回。

無論單獨(dú)編碼數(shù)字布卡,字串還是命令組合雨让,我們都把開頭到第一個(gè)CRLF的內(nèi)容看成頭標(biāo)簽(Token)。

響應(yīng)協(xié)議

既然有請(qǐng)求的協(xié)議規(guī)定忿等,當(dāng)然也有響應(yīng)回復(fù)的協(xié)議栖忠。請(qǐng)求的命令已經(jīng)介紹了三種,其差別就在于參數(shù)的個(gè)數(shù)贸街。對(duì)于響應(yīng)的回復(fù)庵寞,則會(huì)比請(qǐng)求的情況更多。

RESP協(xié)議規(guī)定薛匪,所有答復(fù)的第一個(gè)字節(jié)規(guī)定了響應(yīng)答復(fù)的類型捐川,后除了類型之外,和請(qǐng)求中編碼字符串和數(shù)字的類似逸尖。響應(yīng)大概類型如下:

  • 狀態(tài)回復(fù)(status reply)的第一個(gè)字節(jié)是 +
  • 錯(cuò)誤回復(fù)(error reply)的第一個(gè)字節(jié)是 -
  • 整數(shù)回復(fù)(integer reply)的第一個(gè)字節(jié)是 :
  • 批量回復(fù)(bulk reply)的第一個(gè)字節(jié)是 $
  • 多條批量回復(fù)(multi bulk reply)的第一個(gè)字節(jié)是 *

狀態(tài)回復(fù)

對(duì)于客戶端命令古沥,執(zhí)行一個(gè)查詢或者一個(gè)寫入操作瘸右,通常會(huì)返回操作的是否成功的狀態(tài)判定。狀態(tài)回復(fù)是單行回復(fù)岩齿。例如上面ping命令的返回就是ok太颤。

ok的狀態(tài)的編碼返回為+OK\r\n,雖然OK也是字串盹沈,但是在狀態(tài)中返回龄章,我們只需要知道狀態(tài)結(jié)果,不需要顯示的告訴返回多少字符乞封,后面可以知道做裙,字符串編碼的時(shí)候需要標(biāo)記字符數(shù),純粹是為了讀取socket的時(shí)候肃晚,確定分包邊界菇用。由于狀態(tài)回復(fù)只是單行字串,因此最后一個(gè)CRLF就能確定包的分界陷揪。

錯(cuò)誤回復(fù)

與狀態(tài)回復(fù)類似惋鸥,錯(cuò)誤回復(fù)的第一個(gè)字符是-,然后跟著錯(cuò)誤的類型悍缠。錯(cuò)誤類型之后以一個(gè)空格結(jié)束卦绣,然后就是錯(cuò)誤的信息(msg),錯(cuò)誤信息是一段文字飞蚓,可以包含空格滤港,錯(cuò)誤信息結(jié)束之后也依然是一個(gè)CRLF表示回復(fù)結(jié)束。

例如命令不存在的時(shí)候會(huì)返回錯(cuò)誤:

127.0.0.1:6379> hello
(error) ERR unknown command 'hello'
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> incr hello
(error) ERR value is not an integer or out of range

返回的錯(cuò)誤編碼回復(fù)依次是-ERR unknow command 'hello\r\n'-ERR value is not an integer or out of range\r\n

整數(shù)回復(fù)

所謂整數(shù)回復(fù)趴拧,即返回?cái)?shù)字類型的回復(fù)溅漾。整數(shù)回復(fù)以:開頭,其后跟著數(shù)字著榴,最后以CRLF結(jié)尾添履。這和之前介紹的數(shù)字編碼的方式一樣。

例如:0\r\n:1000\r\n 都是整數(shù)回復(fù)脑又。什么時(shí)候返回?cái)?shù)字類型呢暮胧?一般是incr數(shù)字操作和一些返回?cái)?shù)字表示邏輯的操作,例如DEL EXISTS等问麸。

127.0.0.1:6379> INCRBYFLOAT float 1.2
"1.2"
127.0.0.1:6379> INCRBYFLOAT float 1.2
"2.4"
127.0.0.1:6379> INCRBY integer 2
(integer) 2
127.0.0.1:6379> INCRBY integer 2
(integer) 4
127.0.0.1:6379> DEL integer
(integer) 1

INCRBY 和 INCRBYFLOAT都是自增參數(shù)的數(shù)字往衷,前者返回的是數(shù)字類型,后者返回的是字符串類型严卖。也就是我們接下來介紹的批量回復(fù)席舍。

批量回復(fù)

所謂的批量回復(fù),只redis返回的二進(jìn)制安全的字串哮笆。很大查詢返回都屬于批量回復(fù)来颤。例如上面的例子中汰扭,INCRBYFLOAT的返回即使批量回復(fù)。

批量回復(fù)以$符號(hào)開頭脚曾,然后跟著字符串的長(zhǎng)度值,然后就是CRLF和實(shí)際的字串?dāng)?shù)據(jù)启具,最后以CRLF結(jié)束本讥。和字符串的編碼一模一樣茅糜。

例如查詢命令 get hello捶闸,返回結(jié)果為world的情況下,批量回復(fù)的原始數(shù)據(jù)編碼為$5\r\nworld\r\n消玄。

當(dāng)請(qǐng)求的key的value不存在的時(shí)候薯演,將會(huì)返回-1用于表示長(zhǎng)度的值撞芍。例如get non-existing-key將收到$-1\r\n的返回。對(duì)于這種空回復(fù)跨扮,客戶端語言就可以靈活處理序无,比較推薦的做法就是使用編程語言表示對(duì)象不存在的值,例如python的None衡创,Golang的nil帝嗡。

多批量回復(fù)

常見的返回是批量回復(fù),此外還有一個(gè)多批量回復(fù)璃氢,顧名思義哟玷,就是多個(gè)批量回復(fù)的組合。操作序列或者集合類的數(shù)據(jù)結(jié)構(gòu)一也,就會(huì)返回多批量回復(fù)巢寡。例如上面的smembers set命令的返回將會(huì)是:

*4\r\n$1\r\n1\r\n$3\r\n21.7\r\n$6\r\n\xe4\xb8\x96\xe7\x95\x8c\r\n$5\r\nhello\r\n

這和我們編碼請(qǐng)求的方式一模一樣。由此可見椰苟,RESP的設(shè)計(jì)非常優(yōu)雅抑月。

多批量回復(fù)是批量回復(fù)的組合,那么也會(huì)有返回-1的情況舆蝴。此外爪幻,當(dāng)讀取一個(gè)集合,如果集合沒有元素须误,則不是返回nil挨稿,而是返回空集合的結(jié)果,接多批量回復(fù)的結(jié)果為*0\r\n京痢。

官網(wǎng)的還給出了一個(gè)例子關(guān)于多批量回復(fù)的空元素奶甘。例如下面的多重批量回復(fù):

*3
$3
foo
$-1
$3
bar

其中, 回復(fù)中的第二個(gè)元素為空祭椰。因此最終轉(zhuǎn)換成python的對(duì)象應(yīng)該是這樣的:["foo", None, "bar"]臭家。

總結(jié)

RESP的協(xié)議基于tcp的應(yīng)用層協(xié)議疲陕,主要用到了字符和數(shù)字的編碼和CRLF的組合編碼方式。我們認(rèn)識(shí)到字串的編碼和數(shù)字編碼的方式钉赁。請(qǐng)求的時(shí)候使用類似多批量回復(fù)方式編碼蹄殃,回復(fù)的響應(yīng)有多種,可以根據(jù)第一個(gè)字節(jié)做不同情況的處理你踩,比如狀態(tài)回復(fù)诅岩,錯(cuò)誤回復(fù),批量回復(fù)和多批量回復(fù)带膜。

對(duì)于多批量回復(fù)吩谦,空和無窮的方式也需要考慮,前者會(huì)返回一個(gè)0表示空膝藕,后者會(huì)返回-1表示無窮大(block情況也類似)式廷。

了解了協(xié)議的編碼方式,下一步芭挽,我們就使用代碼實(shí)現(xiàn)這個(gè)協(xié)議的編碼和解碼過程滑废。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市袜爪,隨后出現(xiàn)的幾起案子策严,更是在濱河造成了極大的恐慌,老刑警劉巖饿敲,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妻导,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡怀各,警方通過查閱死者的電腦和手機(jī)倔韭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瓢对,“玉大人寿酌,你說我怎么就攤上這事∷队迹” “怎么了醇疼?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)法焰。 經(jīng)常有香客問我秧荆,道長(zhǎng),這世上最難降的妖魔是什么埃仪? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任乙濒,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘颁股。我一直安慰自己么库,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布甘有。 她就那樣靜靜地躺著诉儒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪亏掀。 梳的紋絲不亂的頭發(fā)上忱反,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音幌氮,去河邊找鬼缭受。 笑死胁澳,一個(gè)胖子當(dāng)著我的面吹牛该互,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播韭畸,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼宇智,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了胰丁?” 一聲冷哼從身側(cè)響起随橘,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锦庸,沒想到半個(gè)月后机蔗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡甘萧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年萝嘁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扬卷。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡牙言,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出怪得,到底是詐尸還是另有隱情咱枉,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布徒恋,位于F島的核電站蚕断,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏入挣。R本人自食惡果不足惜基括,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望财岔。 院中可真熱鬧风皿,春花似錦河爹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至魔眨,卻和暖如春媳维,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背遏暴。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來泰國打工侄刽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人朋凉。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓州丹,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親杂彭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子墓毒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 編碼發(fā)送數(shù)據(jù)到redis服務(wù),客戶端完成了第一個(gè)交互過程亲怠,即請(qǐng)求的過程所计。接下來客戶端還要接受并解析服務(wù)端的響應(yīng)回復(fù)...
    人世間閱讀 1,197評(píng)論 0 1
  • 設(shè)計(jì)模式,即解決特定場(chǎng)景的問題的一系列方法與總結(jié) 程序設(shè)計(jì)六大原則: 一团秽、單一職責(zé)原則:描述的意思是每個(gè)類都只負(fù)責(zé)...
    superzhan1992閱讀 195評(píng)論 0 0
  • 不知道今天自己怎么了 可能病了 連脾氣都跟著壞了 明明沒什么的 都被弄得好像多大一件事的 你也是夠忍得我的了 真是...
    小氣鬼丶Sue閱讀 174評(píng)論 0 0
  • 敬篤 塵世的光主胧,守護(hù)著過往與理想,那些看似陌生的植物习勤,表層的褶皺踪栋,都是歲月無情的傷痕。 人類就像候鳥一樣姻报,每天對(duì)著...
    山谷小道士閱讀 580評(píng)論 0 8
  • 我一直覺得自己的天賦是隨和吴旋,容易被接近,直到有勇氣問別人眼中的自己才發(fā)現(xiàn)原來自己眼中的自己和別人眼中的自己完全不一...
    Irene_274a閱讀 720評(píng)論 0 0