共計 4614 個字符,預計需要花費 12 分鐘才能閱讀完成。
自動寫代碼機器人,免費開通
這篇文章主要介紹 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 協議指的是什么的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注丸趣 TV 行業資訊頻道!
向 AI 問一下細節
丸趣 TV 網 – 提供最優質的資源集合!