Redis 通訊協議的格式
協議說明
Redis協議在以下幾點之間做出了折衷:
- 簡單的實現
- 快速地被計算機解析
- 簡單得可以能被人工解析
-
網路層
Redis在TCP埠6379上監聽到來的連線,客戶端連線到來時,Redis伺服器為此建立一個TCP連線。在客戶端與伺服器端之間傳輸的每個Redis命令或者資料都以\r\n結尾。
請求
Redis接收由不同引數組成的命令。一旦收到命令,將會立刻被處理,並回復給客戶端。
新的統一請求協議
新的統一協議已在Redis 1.2中引入,但是在Redis 2.0中,這就成為了與Redis伺服器通訊的標準方式。
在這個統一協議裡,傳送給Redis服務端的所有引數都是二進位制安全的。以下是通用形式:
*<number of arguments> CR LF
$<number of bytes of argument 1> CR LF
<argument data> CR LF
...
$<number of bytes of argument N> CR LF
<argument data> CR LF
例子如下:
*3
$3
SET
$5
mykey
$7
myvalue
上面的命令看上去像是單引號字串,所以可以在查詢中看到每個位元組的準確值:
"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"
在Redis的回覆中也使用這樣的格式。批量回復時,這種格式用於每個引數$6\r\nmydata\r\n。 實際的統一請求協議是Redis用於返回列表項,並呼叫 Multi-bulk回覆。 僅僅是N個以以*\r\n為字首的不同批量回復,是緊隨的引數(批量回復)數目。
回覆
Redis用不同的回覆型別回覆命令。它可能從伺服器傳送的第一個位元組開始校驗回覆型別:
- 用單行回覆,回覆的第一個位元組將是“+”
- 錯誤訊息,回覆的第一個位元組將是“-”
- 整型數字,回覆的第一個位元組將是“:”
- 批量回復,回覆的第一個位元組將是“$”
- 多個批量回復,回覆的第一個位元組將是“*”
Simple Strings
狀態回覆(或者單行回覆)以“+”開始以“\r\n”結尾的單行字串形式。例如:
"+OK\r\n"
客戶端庫將在“+”後面返回所有資料,正如上例中字串“OK”一樣。
Errors
錯誤回覆傳送類似於狀態回覆。唯一的不同是第一個位元組用“-”代替“+”。
錯誤回覆僅僅在一些意料之外的事情發生時傳送,例如:如果你試圖執行一個操作來應付錯誤的資料型別,或者如果命令不存在等等。所以當收到一個錯誤回覆時,客戶端將會出現一個異常。
Integers
這種回覆型別只是用CRLF結尾字串來表示整型,用一個位元組的“:”作為字首。例如:“:0\r\n”,或者“:1000\r\n”是整型回覆。
像INCR或者LASTAVE命令用整型回覆作為實際回覆值,此時對於返回的整型沒有特殊的意思。它僅僅是為INCR、LASTSAVE的UNIX時間等增加數值。
一些命令像EXISTS將為true返回1,為false返回0。
其它命令像SADD、SREM和SETNX如果操作實際完成了的話將返回1,否則返回0。
接下來的命令將回復一個整型回覆:SETNX、DEL、EXISTS、INCR、INCRBY、DECR、DECRBY、DBSIZE、LASTSAVE、RENAMENX、MOVE、LLEN、SADD、SREM、SISMEMBER、SCARD。
Bulk Strings
批量回覆被伺服器用於返回一個單二進位制安全字串。
C: GET mykey
S: $6\r\nfoobar\r\n
伺服器傳送第一行回覆,該行以“$”開始後面跟隨實際要傳送的位元組數,隨後是CRLF,然後傳送實際資料,隨後是2個位元組的額外資料用於最後的CRLF。伺服器傳送的準確序列如下:
"$6\r\nfoobar\r\n"
如果請求的值不存在,批量回復將使用特殊的值-1來作為資料長度,例如:
C: GET nonexistingkey
S: $-1
當請求的物件不存在時,客戶端庫API不會返回空字串,而會返回空物件。例如:Ruby庫返回‘nil’,而C庫返回NULL(或者在回覆的物件裡設定指定的標誌)等等。
Arrays
像命令LRNGE需要返回多個值(列表的每個元素是一個值,而LRANGE需要返回多於一個單元素)。使用多批量寫是有技巧的,用一個初始行作為字首來指示多少個批量寫緊隨其後。批量回復的第一個位元組總是*,例如:
C: LRANGE mylist 0 3
s: *4
s: $3
s: foo
s: $3
s: bar
s: $5
s: Hello
s: $5
s: World
正如您可以看到的多批量回復是以完全相同的格式使用Redis統一協議將命令傳送給伺服器。
伺服器傳送的第一行是*4\r\n,用於指定緊隨著4個批量回復。然後傳送每個批量寫。
如果指定的鍵不存在,則該鍵被認為是持有一個空的列表,且數值0被當作多批量計數值來發送,例如:
C: LRANGE nokey 0 1
S: *0
當BLPOP命令超時時,它返回nil多批量回復。這種型別多批量回復的計數器是-1,且值被當作nil來解釋。例如:
C: BLPOP key 1
S: *-1
當這種情況發生時,客戶端庫API將返回空nil物件,且不是一個空列表。這必須有別於空列表和錯誤條件(例如:BLPOP命令的超時條件)。
多批量回復中的Nil元素
多批量回復的單元素長度可能是-1,為了發出訊號這個元素被丟失且不是空字串。這種情況傳送在SORT命令時,此時使用GET模式選項且指定的鍵丟失。一個多批量回復包含一個空元素的例子如下:
S: *3
S: $3
S: foo
S: $-1
S: $3
S: bar
第二個元素是空。客戶端庫返回如下:
["foo",nil,"bar"]
多命令和管道
客戶端能使用同樣條件為了發出多個命令。管道用於支援多命令能夠被客戶端用單寫操作來發送,它不需要為了傳送下一條命令而讀取伺服器的回覆。所有回覆都能在最後被讀出。
通常Redis伺服器和客戶端擁有非常快速的連線,所以在客戶端的實現中支援這個特性不是那麼重要,如果一個應用需要在短時間內發出大量的命令,管道仍然會非常快。
舊協議傳送命令
在統一請求協議出現前,Redis用不同的協議傳送命令,現在仍然支援,它簡單通過手動telnet。在這種協議中,有兩種型別的命令:
- 內聯命令:簡單命令其引數用空格分割字串。非二進位制安全。
- 批量命令:批量命令準確如內聯命令,但是最後的引數用特殊方式來處理用於保證最後引數二進位制安全。 內聯命令
最簡單的傳送Redis命令的方式是通過內聯命令。下面是一個使用內聯命令聊天的伺服器/客戶端的例子(伺服器聊天用S:開始,客戶端聊天用C:開始)。
C: PING
S: +PONG
下面是另外一個內聯命令返回整數的例子:
C: EXISTS somekey
S: :0
因為‘somekey’不存在,所以伺服器返回‘:0’。
注意:EXISTS命令帶有一個引數。引數用空格分割。
批量命令
一些命令當用內聯命令傳送時需要一種特殊的格式用於支援最後引數二進位制安全。這種命令用最後引數作為“位元組計數器”,然後傳送批量資料(因為伺服器知道讀取多少個位元組,所以是二進位制安全的)。
請看下面的例子:
C: SET mykey 6
C: foobar
S: +OK
這條命令的最後一個引數是‘6’。這用於指定隨後資料的位元組數,即字串“foobar”。注意:雖然這個位元組流是以額外的兩個CRLF位元組結尾的。
所有批量命令都是用這種準確的格式:用隨後資料的位元組數代替最後一個引數,緊跟著後面是組成引數本身的位元組和CRLF。為了更清楚程式,下面是通過客戶端傳送字串的例子:
"SET mykey 6\r\nfoobar\r\n"
Redis有一個內部列表,用於表示哪些命令是內聯,哪些命令是批量,所以你不得不傳送相應的命令。強烈建議使用新的統一請求協議來代替老的協議。