1. 程式人生 > >Redis協議規範(譯文)

Redis協議規範(譯文)

Redis客戶端使用名為RESP(Redis序列化協議)的協議與Redis伺服器進行通訊。 雖然該協議是專為Redis設計的,但它可以用於其他CS軟體專案的通訊協議。

RESP是以下幾方面的考慮:
- 易於實現
- 快速解析
- 可讀性高

RESP可以序列化不同的資料型別,如整型,字串,陣列。 還有一種特定的錯誤型別。 請求將要執行的命令作為字串陣列從Redis客戶端傳送到Redis伺服器。Redis使用特定資料型別的命令進行回覆。

RESP是二進位制安全的,不需要處理從一個程序傳輸到另一個程序的批量資料,因為它使用字首長度來傳輸批量資料。

注意: 此處概述的協議僅用於客戶端 - 伺服器通訊。 Redis Cluster使用不同的二進位制協議,以便在節點之間交換訊息。

網路層

客戶端連線到Redis伺服器,是建立TCP連線到埠6379。
雖然RESP在技術上是非TCP特定的,但在Redis的上下文中,協議僅用於TCP連線(或類似的面向流的連線,如Unix套接字)。

請求 - 響應模型

Redis接受由不同引數組成的命令。 收到命令後,將對其進行處理並將回覆傳送回客戶端。
這是最簡單的模型,但有兩個例外:
- Redis支援流水線操作(本文件稍後介紹)。 因此,客戶端可以一次傳送多個命令,並等待稍後的回覆。
- 當Redis客戶端處於 Pub/Sub 時,協議會更改語義併成為推送協議,即客戶端不再需要傳送命令,因為伺服器會在它們接收到命令時發自動向客戶端傳送新訊息。

排除上述兩個例外,Redis協議是一個簡單的請求 - 響應協議。

RESP 協議描述

RESP協議在Redis 1.2中引入,但它成為與Redis 2.0中的Redis伺服器通訊的標準方式。 這是每一個Redis客戶端中應該實現的協議。

RESP實際上是一個支援以下資料型別的序列化協議:單行字串,錯誤資訊,整型,多行字串和陣列。
RESP在Redis中用作請求 - 響應協議的方式如下:
- 客戶端將命令作為字串陣列傳送到Redis伺服器。
- 伺服器根據命令實現回覆一種RESP型別資料。

在 RESP 中, 一些資料的型別通過它的第一個位元組進行判斷:

  • 單行回覆:回覆的第一個位元組是 “+”

  • 錯誤資訊:回覆的第一個位元組是 “-“

  • 整形數字:回覆的第一個位元組是 “:”

  • 多行字串:回覆的第一個位元組是 “$”

  • 陣列:回覆的第一個位元組是 “*”

此外,RESP能夠使用稍後指定的Bulk Strings或Array的特殊變體來表示Null值。
在RESP中,協議的不同部分始終以“\ r \ n”(CRLF)結束。

RESP 單行字串(簡單字串)

簡單字串按以下方式編碼:加號字元,後跟不能包含CR或LF字元的字串(不允許換行),由CRLF終止(即“\ r \ n”)。

Simple Strings用於以最小的開銷傳輸非二進位制安全字串。 例如,許多Redis命令成功回覆時只有“OK”,因為RESP 單行字串使用以下5個位元組進行編碼:

"+OK\r\n"

為了傳送二進位制安全字串,使用RESP 多行字串代替。

當Redis使用Simple String回覆時,客戶端庫應該向呼叫者返回一個字串,該字串由“+”之後的第一個字元組成,直到字串結尾,不包括最終的CRLF位元組。

RESP 錯誤資訊

RESP具有錯誤的特定資料型別。 實際上錯誤與RESP 單行字串完全相同,但第一個字元是減號’ - ‘字元而不是加號。

RESP中單行字串和錯誤之間的真正區別在於客戶端將錯誤視為異常,組成錯誤型別的字串是錯誤訊息本身。
基本格式如下:

"-Error message\r\n"

錯誤回覆僅在發生錯誤時傳送,例如,如果您嘗試對錯誤的資料型別執行操作,或者命令不存在等等。 收到錯誤回覆時,客戶端應將異常丟擲。

以下是錯誤回覆的示例:

-ERR unknown command 'foobar'
-WRONGTYPE Operation against a key holding the wrong kind of value

“ - ”之後的第一個單詞,直到第一個空格或換行符,表示返回的錯誤型別。 這只是Redis使用的約定,不是RESP錯誤格式的一部分。

例如,ERR是一般錯誤,而WRONGTYPE是一個更具體的錯誤,意味著客戶端嘗試對錯誤的資料型別執行操作。 這稱為錯誤字首,是一種允許客戶端理解伺服器返回的錯誤型別的方法,而不依賴於給定的確切訊息,這可能隨時間而變化。

客戶端實現可以針對不同的錯誤返回不同型別的異常,或者可以通過直接將錯誤名稱作為字串提供給呼叫者來提供捕獲錯誤的通用方法。

但是,這樣的功能不應該被認為是至關重要的,因為它很少有用,並且有限的客戶端實現可能只返回通用的錯誤條件,例如false。

RESP 整型資料

此型別只是一個CRLF終止的字串,表示一個以“:”位元組為字首的整數。 例如“:0 \ r \ n”或“:1000 \ r \ n”是整數回覆。
許多Redis命令返回RESP 整型,如INCR,LLEN和LASTSAVE。

返回的整數沒有特殊含義,它只是INCR的增量編號,LASTSAVE的UNIX時間等等。 但是,返回的整數應保證在有符號的64位整數範圍內。

整數回覆也被廣泛使用,以便返回真或假。 例如,EXISTS或SISMEMBER之類的命令將返回1表示true,0表示false。

如果實際執行操作,其他命令(如SADD,SREM和SETNX)將返回1,否則返回0。

以下命令將回復整數回覆:SETNX,DEL,EXISTS,INCR,INCRBY,DECR,DECRBY,DBSIZE,LASTSAVE,RENAMENX,MOVE,LLEN,SADD,SREM,SISMEMBER,SCARD。

RESP 多行字串

多行字串用於表示長度最大為512 MB的單個二進位制安全字串。

多行字串按以下方式編碼:

  • 一個“$”位元組後跟組成字串的位元組數(一個字首長度),由CRLF終止。
  • 字串資料。
  • 最終的CRLF。

所以字串“foobar”的編碼如下:

"$6\r\nfoobar\r\n"

當只是一個空字串時:

"$0\r\n\r\n"

RESP 多行字串也可用於使用用於表示Null值的特殊格式來表示值的不存在。 在這種特殊格式中,長度為-1,並且沒有資料,因此Null表示為:

"$-1\r\n"

當伺服器使用Null 多行字串回覆時,客戶端庫API不應返回空字串,而應返回nil物件。 例如,Ruby庫應返回’nil’,而C庫應返回NULL(或在reply物件中設定特殊標誌),依此類推。

RESP 陣列

客戶端使用RESP 陣列將命令傳送到Redis伺服器。 類似地,某些Redis命令將元素集合返回給客戶端使用RESP 陣列是回覆型別。 一個例子是LRANGE命令,它返回列表的元素。

RESP陣列使用以下格式傳送:

  • *字元作為第一個位元組,後跟陣列中的元素數作為十進位制數,後跟CRLF。
  • 陣列的每個元素的附加RESP型別。

所以空陣列就是以下內容:

"*0\r\n"

那麼兩個RESP批量字串“foo”和“bar”的陣列編碼為:

"*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"

正如您在陣列前面加上* CRLF部分之後所看到的那樣,組成陣列的其他資料型別將一個接一個地連線起來。 例如,三個整數的陣列編碼如下:

"*3\r\n:1\r\n:2\r\n:3\r\n"

陣列可以包含混合型別,元素不必具有相同的型別。 例如,四個整數和批量字串的列表可以編碼如下:

*5\r\n
:1\r\n
:2\r\n
:3\r\n
:4\r\n
$6\r\n
foobar\r\n

伺服器傳送的第一行是* 5 \ r \ n,以指定將跟隨五個回覆。 然後傳送構成多重回復專案的每個回覆。

Null 陣列的概念也存在,並且是指定Null值的替代方法(通常使用Null 多行字串,但由於歷史原因,我們有兩種格式)。

例如,當BLPOP命令超時時,它返回一個計數為-1的Null陣列,如下例所示:

"*-1\r\n"

當Redis使用Null陣列回覆時,客戶端庫API應返回空物件而不是空陣列。 這是區分空列表和不同條件(例如BLPOP命令的超時條件)所必需的。

RESP中可以使用陣列中巢狀陣列。 例如,兩個陣列的陣列編碼如下:

*2\r\n
*3\r\n
:1\r\n
:2\r\n
:3\r\n
*2\r\n
+Foo\r\n
-Bar\r\n

第二個元素是Null。 客戶端庫應返回如下內容:

["foo",nil,"bar"]

注意,這不是前面部分中所述的例外,而只是進一步指定協議的示例。

傳送命令到 Redis 服務端

既然熟悉RESP序列化格式,那麼編寫Redis客戶端庫的實現將很容易。 我們可以進一步講述客戶端和伺服器之間的互動如何工作:

  • 客戶端向Redis伺服器傳送僅由Bulk Strings組成的RESP陣列。
  • Redis伺服器回覆傳送任何有效RESP資料型別作為回覆的客戶端。

因此,例如,典型的互動可以是以下所示。

客戶端傳送命令LLEN mylist以獲取儲存在金鑰mylist中的列表長度,伺服器回覆一個Integer回覆,如下例所示(C:是客戶端,S:伺服器)。

C: *2\r\n
C: $4\r\n
C: LLEN\r\n
C: $6\r\n
C: mylist\r\n
S: :48293\r\n

通常我們將協議的不同部分與換行符分開以簡化,但實際的互動是客戶端傳送 * 2 \ r \ n 4rnLLENrn 6 \ r \ nmylist \ r \ n 整體。

小結

這是樓主第一次嘗試翻譯一篇技術文件,相對來說技術文件的英文閱讀起來還是比較舒服的,相信有了第一次嘗試,之後肯定會越來越順利。由於樓主水平有限,文章中難免有紕漏,期望小夥伴的指出,感謝……。

參考連結