Redis通信协议(protocol)
Redis通信協(xié)議(protocol)
本文檔翻譯自:?http://redis.io/topics/protocol?。
Redis 協(xié)議在以下三個目標之間進行折中:
- 易于實現(xiàn)
- 可以高效地被計算機分析(parse)
- 可以很容易地被人類讀懂
網(wǎng)絡(luò)層
客戶端和服務器通過 TCP 連接來進行數(shù)據(jù)交互, 服務器默認的端口號為 6379 。
客戶端和服務器發(fā)送的命令或數(shù)據(jù)一律以?\r\n?(CRLF)結(jié)尾。
請求
Redis 服務器接受命令以及命令的參數(shù)。
服務器會在接到命令之后,對命令進行處理,并將命令的回復傳送回客戶端。
新版統(tǒng)一請求協(xié)議
新版統(tǒng)一請求協(xié)議在 Redis 1.2 版本中引入, 并最終在 Redis 2.0 版本成為 Redis 服務器通信的標準方式。
你的 Redis 客戶端應該按照這個新版協(xié)議來進行實現(xiàn)。
在這個協(xié)議中, 所有發(fā)送至 Redis 服務器的參數(shù)都是二進制安全(binary safe)的。
以下是這個協(xié)議的一般形式:
*<參數(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譯注:命令本身也作為協(xié)議的其中一個參數(shù)來發(fā)送。
舉個例子, 以下是一個命令協(xié)議的打印版本:
*3 $3 SET $5 mykey $7 myvalue這個命令的實際協(xié)議值如下:
"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"稍后我們會看到, 這種格式除了用作命令請求協(xié)議之外, 也用在命令的回復協(xié)議中: 這種只有一個參數(shù)的回復格式被稱為批量回復(Bulk Reply)。
統(tǒng)一協(xié)議請求原本是用在回復協(xié)議中, 用于將列表的多個項返回給客戶端的, 這種回復格式被稱為多條批量回復(Multi Bulk Reply)。
一個多條批量回復以?*<argc>\r\n?為前綴, 后跟多條不同的批量回復, 其中?argc?為這些批量回復的數(shù)量。
回復
Redis 命令會返回多種不同類型的回復。
通過檢查服務器發(fā)回數(shù)據(jù)的第一個字節(jié), 可以確定這個回復是什么類型:
- 狀態(tài)回復(status reply)的第一個字節(jié)是?"+"
- 錯誤回復(error reply)的第一個字節(jié)是?"-"
- 整數(shù)回復(integer reply)的第一個字節(jié)是?":"
- 批量回復(bulk reply)的第一個字節(jié)是?"$"
- 多條批量回復(multi bulk reply)的第一個字節(jié)是?"*"
狀態(tài)回復
一個狀態(tài)回復(或者單行回復,single line reply)是一段以?"+"?開始、?"\r\n"?結(jié)尾的單行字符串。
以下是一個狀態(tài)回復的例子:
+OK客戶端庫應該返回?"+"?號之后的所有內(nèi)容。 比如在在上面的這個例子中, 客戶端就應該返回字符串?"OK"?。
狀態(tài)回復通常由那些不需要返回數(shù)據(jù)的命令返回,這種回復不是二進制安全的,它也不能包含新行。
狀態(tài)回復的額外開銷非常少,只需要三個字節(jié)(開頭的?"+"?和結(jié)尾的 CRLF)。
錯誤回復
錯誤回復和狀態(tài)回復非常相似, 它們之間的唯一區(qū)別是, 錯誤回復的第一個字節(jié)是?"-"?, 而狀態(tài)回復的第一個字節(jié)是?"+"?。
錯誤回復只在某些地方出現(xiàn)問題時發(fā)送: 比如說, 當用戶對不正確的數(shù)據(jù)類型執(zhí)行命令, 或者執(zhí)行一個不存在的命令, 等等。
一個客戶端庫應該在收到錯誤回復時產(chǎn)生一個異常。
以下是兩個錯誤回復的例子:
-ERR unknown command 'foobar' -WRONGTYPE Operation against a key holding the wrong kind of value在?"-"?之后,直到遇到第一個空格或新行為止,這中間的內(nèi)容表示所返回錯誤的類型。
ERR?是一個通用錯誤,而?WRONGTYPE?則是一個更特定的錯誤。 一個客戶端實現(xiàn)可以為不同類型的錯誤產(chǎn)生不同類型的異常, 或者提供一種通用的方式, 讓調(diào)用者可以通過提供字符串形式的錯誤名來捕捉(trap)不同的錯誤。
不過這些特性用得并不多, 所以并不是特別重要, 一個受限的(limited)客戶端可以通過簡單地返回一個邏輯假(false)來表示一個通用的錯誤條件。
整數(shù)回復
整數(shù)回復就是一個以?":"?開頭, CRLF 結(jié)尾的字符串表示的整數(shù)。
比如說,?":0\r\n"?和?":1000\r\n"?都是整數(shù)回復。
返回整數(shù)回復的其中兩個命令是?INCR?和?LASTSAVE?。 被返回的整數(shù)沒有什么特殊的含義,?INCR?返回鍵的一個自增后的整數(shù)值, 而?LASTSAVE?則返回一個 UNIX 時間戳, 返回值的唯一限制是這些數(shù)必須能夠用 64 位有符號整數(shù)表示。
整數(shù)回復也被廣泛地用于表示邏輯真和邏輯假: 比如?EXISTS?和?SISMEMBER?都用返回值?1?表示真,?0?表示假。
其他一些命令, 比如?SADD?、?SREM?和?SETNX?, 只在操作真正被執(zhí)行了的時候, 才返回?1?, 否則返回?0?。
以下命令都返回整數(shù)回復:?SETNX?、?DEL?、?EXISTS?、?INCR?、?INCRBY?、?DECR?、?DECRBY?、?DBSIZE?、?LASTSAVE?、RENAMENX?、?MOVE?、?LLEN?、?SADD?、?SREM?、?SISMEMBER?、?SCARD?。
批量回復
服務器使用批量回復來返回二進制安全的字符串,字符串的最大長度為 512 MB 。
客戶端:GET mykey 服務器:foobar服務器發(fā)送的內(nèi)容中:
- 第一字節(jié)為?"$"?符號
- 接下來跟著的是表示實際回復長度的數(shù)字值
- 之后跟著一個 CRLF
- 再后面跟著的是實際回復數(shù)據(jù)
- 最末尾是另一個 CRLF
對于前面的?GET?命令,服務器實際發(fā)送的內(nèi)容為:
"$6\r\nfoobar\r\n"如果被請求的值不存在, 那么批量回復會將特殊值?-1?用作回復的長度值, 就像這樣:
客戶端:GET non-existing-key 服務器:$-1這種回復稱為空批量回復(NULL Bulk Reply)。
當請求對象不存在時,客戶端應該返回空對象,而不是空字符串: 比如 Ruby 庫應該返回?nil?, 而 C 庫應該返回?NULL?(或者在回復對象中設(shè)置一個特殊標志), 諸如此類。
多條批量回復
像?LRANGE?這樣的命令需要返回多個值, 這一目標可以通過多條批量回復來完成。
多條批量回復是由多個回復組成的數(shù)組, 數(shù)組中的每個元素都可以是任意類型的回復, 包括多條批量回復本身。
多條批量回復的第一個字節(jié)為?"*"?, 后跟一個字符串表示的整數(shù)值, 這個值記錄了多條批量回復所包含的回復數(shù)量, 再后面是一個 CRLF 。
客戶端: LRANGE mylist 0 3 服務器: *4 服務器: $3 服務器: foo 服務器: $3 服務器: bar 服務器: $5 服務器: Hello 服務器: $5 服務器: World在上面的示例中,服務器發(fā)送的所有字符串都由 CRLF 結(jié)尾。
正如你所見到的那樣, 多條批量回復所使用的格式, 和客戶端發(fā)送命令時使用的統(tǒng)一請求協(xié)議的格式一模一樣。 它們之間的唯一區(qū)別是:
- 統(tǒng)一請求協(xié)議只發(fā)送批量回復。
- 而服務器應答命令時所發(fā)送的多條批量回復,則可以包含任意類型的回復。
以下例子展示了一個多條批量回復, 回復中包含四個整數(shù)值, 以及一個二進制安全字符串:
*5\r\n :1\r\n :2\r\n :3\r\n :4\r\n $6\r\n foobar\r\n在回復的第一行, 服務器發(fā)送?*5\r\n?, 表示這個多條批量回復包含 5 條回復, 再后面跟著的則是 5 條回復的正文。
多條批量回復也可以是空白的(empty), 就像這樣:
客戶端: LRANGE nokey 0 1 服務器: *0\r\n無內(nèi)容的多條批量回復(null multi bulk reply)也是存在的, 比如當?BLPOP?命令的阻塞時間超過最大時限時, 它就返回一個無內(nèi)容的多條批量回復, 這個回復的計數(shù)值為?-1?:
客戶端: BLPOP key 1 服務器: *-1\r\n客戶端庫應該區(qū)別對待空白多條回復和無內(nèi)容多條回復: 當 Redis 返回一個無內(nèi)容多條回復時, 客戶端庫應該返回一個 null 對象, 而不是一個空數(shù)組。
多條批量回復中的空元素
多條批量回復中的元素可以將自身的長度設(shè)置為?-1?, 從而表示該元素不存在, 并且也不是一個空白字符串(empty string)。
當?SORT?命令使用?GET?pattern?選項對一個不存在的鍵進行操作時, 就會發(fā)生多條批量回復中帶有空白元素的情況。
以下例子展示了一個包含空元素的多重批量回復:
服務器: *3 服務器: $3 服務器: foo 服務器: $-1 服務器: $3 服務器: bar其中, 回復中的第二個元素為空。
對于這個回復, 客戶端庫應該返回類似于這樣的回復:
["foo", nil, "bar"]多命令和流水線
客戶端可以通過流水線, 在一次寫入操作中發(fā)送多個命令:
- 在發(fā)送新命令之前, 無須閱讀前一個命令的回復。
- 多個命令的回復會在最后一并返回。
內(nèi)聯(lián)命令
當你需要和 Redis 服務器進行溝通, 但又找不到?redis-cli?, 而手上只有?telnet?的時候, 你可以通過 Redis 特別為這種情形而設(shè)的內(nèi)聯(lián)命令格式來發(fā)送命令。
以下是一個客戶端和服務器使用內(nèi)聯(lián)命令來進行交互的例子:
客戶端: PING 服務器: +PONG以下另一個返回整數(shù)值的內(nèi)聯(lián)命令的例子:
客戶端: EXISTS somekey 服務器: :0因為沒有了統(tǒng)一請求協(xié)議中的?"*"?項來聲明參數(shù)的數(shù)量, 所以在?telnet?會話輸入命令的時候, 必須使用空格來分割各個參數(shù), 服務器在接收到數(shù)據(jù)之后, 會按空格對用戶的輸入進行分析(parse), 并獲取其中的命令參數(shù)。
高性能 Redis 協(xié)議分析器
盡管 Redis 的協(xié)議非常利于人類閱讀, 定義也很簡單, 但這個協(xié)議的實現(xiàn)性能仍然可以和二進制協(xié)議一樣快。
因為 Redis 協(xié)議將數(shù)據(jù)的長度放在數(shù)據(jù)正文之前, 所以程序無須像 JSON 那樣, 為了尋找某個特殊字符而掃描整個 payload , 也無須對發(fā)送至服務器的 payload 進行轉(zhuǎn)義(quote)。
程序可以在對協(xié)議文本中的各個字符進行處理的同時, 查找 CR 字符, 并計算出批量回復或多條批量回復的長度, 就像這樣:
#include <stdio.h>int main(void) {unsigned char *p = "$123\r\n";int len = 0;p++;while(*p != '\r') {len = (len*10)+(*p - '0');p++;}/* Now p points at '\r', and the len is in bulk_len. */printf("%d\n", len);return 0; }得到了批量回復或多條批量回復的長度之后, 程序只需調(diào)用一次?read?函數(shù), 就可以將回復的正文數(shù)據(jù)全部讀入到內(nèi)存中, 而無須對這些數(shù)據(jù)做任何的處理。
在回復最末尾的 CR 和 LF 不作處理,丟棄它們。
Redis 協(xié)議的實現(xiàn)性能可以和二進制協(xié)議的實現(xiàn)性能相媲美, 并且由于 Redis 協(xié)議的簡單性, 大部分高級語言都可以輕易地實現(xiàn)這個協(xié)議, 這使得客戶端軟件的 bug 數(shù)量大大減少。
from:?http://redisdoc.com/topic/protocol.html
總結(jié)
以上是生活随笔為你收集整理的Redis通信协议(protocol)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis事务(transaction)
- 下一篇: Redis发布与订阅(pub/sub)