您好,登錄后才能下訂單哦!
這篇文章主要介紹redis協議指的是什么,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
Redis客戶端通過使用一種叫RESP(REdis Serialization Protocol, redis序列化協議)協議與Redis服務器交互。雖然這個協議是為Redis而設計的,但它也可以用于其他client-server架構的軟件系統。(譯注: 從一些公開的資料來看,陌陌的IM協議設計就參考了Redis協議)
RESP 權衡了以下幾個方面:
實現要簡單解析要快方便人閱讀
RESP可以序列化不同的數據類型,像integers、strings、arrays,對于錯誤也設計了特殊的類型。客戶端以字符串參數數組的請求形式發送給Redis服務器執行,Redis返回命令相關的數據類型。
RESP是二進制安全(binary-safe)的,并且不需要解析由一個進程發送給另一個進程的bulk 數據,因為它使用長度前綴來傳輸bulk 數據。
注意:這里所說的協議只用于client-server的通信。Redis Cluster使用不同的二進制協議在node間進行消息交互。
網絡層
客戶端通過建立端口為6379的TCP連接與Redis通信。
雖然RESP從技術上來說并不是TCP相關的,但對Redis來說該協議只用于TCP(或者其他流式協議如Unix域協議)。 (譯注:反觀memcached, 既支持tcp, 也支持udp, 但實際上生產環境基本只用tcp。我認為這是一種過度設計了,搞不好還可能被駭客利用來做 memcached udp 反射攻擊。。。)
請求響應模型
Redis 接收不同參數構成的命令。當命令接收到后就會被處理,然后響應發送給客戶端。
這是最簡單的模型了,但有兩點例外:
Redis支持 pipelining (后文會提及)。所以客戶端可以一次發送多個命令,然后等待響應。當客戶端訂閱了一個 Pub/Sub channel, 該協議會改變語意而變成一個推送協議,也就是說客戶端不用發送命令,因為服務端在收到消息后會自動給客戶端發送新的消息(對客戶端訂閱了的channel)。
除了這兩點,Redis協議就是一個簡單的請求-響應協議。
RESP協議描述
RESP協議在Redis 1.2引入,但它現在成為Redis 2.0的標準交互協議。你應該實現Redis客戶端時采用該協議。
RESP事實上是一個支持以下類型的序列化協議:Simple Strings, Errors, Integers, Bulk Strings 和 Arrays。
RESP作為一種請求響應協議,在Redis中使用的方式如下:
客戶端發送命令到Redis服務器,以RESP Bulk Strings數組的方式。服務器根據不同的命令實現,返回相應的RESP實現。
在RESP中,一些數據的類型由第一個字節確定:
對于SImple Strings 響應的第一個字節是“+”對于Errors 響應的第一個字節是 "-"對于Integers 響應第一個字節是 ":"對于Bulk Strings 響應第一個字節是 "$" 對于Arrays 響應第一個字節是 "*"
另外RESP可以用特殊的Bulk String或數組來表示 Null 值,后文會提及。
在RESP中協議不同部分總是用 "\r\n" (CRLF) 分隔。
RESP Simple Strings
Simple Strings通過以下方式編碼:一個加號,后跟一個字符串, 字符串不包含CR或LF字符(不能有換行),以CRLF ("\r\n")結束。
SImple Strings 以最小的代價來傳輸非二進制安全的字符串。例如很多Redis命令在成功時響應"OK",就是用RESP Simple String 來編碼的5個字節:
"+OK\r\n"
為了傳輸二進制安全的字符串,要用RESP Bulk Strings。
當Redis響應一個Simple String時,客戶端庫應該給調用者返回從第一個"+"字符到字符串結尾的字符串,不包括CRLF 字節。
RESP Errors
RESP 對于error有一種特殊的數據類型。實際上error就像RESP Simple String一樣,但第一個字符串是"-"而不是加號。在RESP中 Simple Strings和Errors兩者的真正區別在于errors被客戶端作為異常,構成Error類型的字符串就是字符串本身。基本格式是:
"-Error message\r\n"
Error響應只會在有錯誤發生時才會發送,例如你操作了錯誤的數據類型,或者命令不存在等。當收到Error響應時,客戶端應該拋出一個異常。
以下是error響應的例子:
-ERR unknown command 'foobar'-WRONGTYPE Operation against a key holding the wrong kind of value
在"-"到第一個空格或新行間的第一個詞,表示返回的錯誤類型。這只是Redis自己的一種約定,并不是RESP Error 所規定的格式。
例如,ERR 是通用錯誤,而 WRONGTYPE 是一種更加具體的錯誤,表示客戶端嘗試操作錯誤的數據類型。這稱為 Error Prefix (Error前綴),客戶端可從此得知服務器返回錯誤的類型而不需依賴于那個確切的消息描述,后者會隨時改變。
一個客戶端的實現可能對不同的error返回不同類型的異常,或者向調用者返回代表錯誤的字符串。然而這種特性并不是必須的,因為這并沒什么卵用,一些精簡的客戶端實現可能簡單的返回一般的錯誤情況,例如 false。
RESP Integers
這種類型就是一個代表整數的以CRLF結尾的字符串,并以“:”字節開頭。例如 ":0\r\n", 或 ":1000\r\n" 都是整數響應。
很多Redis命令返回RESP Integers, 像 INCR, LLEN 和 LASTSAVE。
返回的整數并沒什么特殊的含義,它就是 INCR 增加后的數字,LASTSAVE 的UNIX時間戳等。但返回的整數可以保證是在64位有符號整數的范圍內。
整數響應也被大量的用于表示true或false。例如EXISTS和 SISMEMBER 等命令會返回1表示true, 0表示false。
以下命令會返回一個整數: SETNX, DEL, EXISTS, INCR, INCRBY, DECR, DECRBY, DBSIZE, LASTSAVE, RENAMENX, MOVE, LLEN, SADD, SREM, SISMEMBER, SCARD。
RESP Bulk Strings
Bulk Strings 用于表示一個二進制安全的字符串,最大長度為512M。
Bulk Strings 的編碼格式如下:
“$” 后跟字符串字節數(prefix length),以CRLF結束實際的字符串CRLF結束
所以字符串"foobar" 被編碼成:
"$6\r\nfoobar\r\n"
空字符串:
"$0\r\n\r\n"
RESP Bulk String 也可以用一種代表Null值的特殊格式來表示不存在的值。這種特殊格式的長度值為-1, 并且沒數據,所以Null表示為:
"$-1\r\n"
這稱為 Null Bulk String。
當服務器返回Null Bulk String時,客戶端API不應該返回空串,而是nil 對象。例如Ruby庫應該返回 'nil' 而 C 庫應該返回 NULL (或在返回的對象設置特殊的標記),等等。
RESP Arrays
客戶端用RESP Arrays向Redis服務器發送命令。同樣某些Redis命令要返回一個元素集合時也使用RESP Arrays作為返回的類型。一個例子是LRANGE 命令返回一個元素列表。
RESP Arrays使用以下格式發送:
“*” 為第一個字節,后跟數組的元素個數,然后CRLF。然后是數組中的每一個RESP類型表示的元素。
例如一個空數組表示為:
"*0\r\n"
而有兩個RESP Bulk Strings "foo" 和 "bar" 的數組編碼為:
"*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"
正如你所見,在數組前面的 *<count>CRLF 后,數組中的其他的數據類型一個接一個的連接在一起。例如一個有三個整數的數組編碼如下:
"*3\r\n:1\r\n:2\r\n:3\r\n"
數組可以包含混合類型,它不要求所有的元素都是相同的類型。例如,一個有4個interges和1個bulk string的數組可以編碼為:
*5\r\n :1\r\n :2\r\n :3\r\n :4\r\n $6\r\n foobar\r\n
(為清晰起見響應被分為多行)。
服務器發送的第一行 *5\r\n 表示后跟有5個響應,然后每個代表元素的響應被發送。
Null 數組的概念同樣存在,它是Null值的替代方式 (通常使用Null Bulk String,但由于歷史原因我們有兩種格式)。
例如當BLPOP命令超時,它返回一個長度為-1的Null 數組,如下所示:
"*-1\r\n"
在服務端返回Null數組時,客戶端庫API應該返回null對象而不是空數組。區分返回空的列表與其他的情況(如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
高效解析Redis協議
盡管Redis協議非常可讀并且容易實現,它卻可以兼得二進制協議的高效。
RESP使用長度前綴來傳輸bulk 數據,所以不需要像JSON一樣掃描數據負載中的特殊符號,或者用引號括住數據負載。
Bulk和Multi Bulk長度的處理可以一次處理一個字符,同時可以掃描CR字符,像如下的C代碼:
#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; }
當第一個CR被識別后,后面的LF可以忽略不處理。然后bulk數據可以一次讀取而不需要分析 數據負載。最后剩下的CR和LF字符串可以丟棄不處理。
與二進制協議比較性能時,Redis協議在大部分的高級語言實現起來足夠簡單,減少了客戶端軟件的bug數量。
注:
1. 協議中的CR和LF相當于分割符,命令間存在多個CRLF不應影響后續解析,應為多個CRLF應被忽略掉。例如:
$> (printf "PING\r\nPING\r\nPING\r\n\r\n\rPING\r\n"; sleep 1) | nc localhost 6379 +PONG +PONG +PONG +PONG
2. 對比一下memcached的協議,redis的協議確實設計得比較精簡:
(1) 一致的請求形式。redis的請求都是以 Bluk String 數組發送,不同命令只是數組的元素個數不同,所有命令的處理可以先讀取完整個數組再根據不同命令解析數組的參數;而不是像mc協議一樣,不同請求的命令格式不同,那么在讀取網絡字節流的過程中就要對不同命令做不同的處理,增加了協議解析的難度。
(2) 長度前綴是高效解析協議的關鍵。字段長度信息并不是二進制協議的專利,文本協議也可以有。
以上是redis協議指的是什么的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。