理解 Redis 的 RESP 協議

TaoBeier發表於2017-03-05

簡介

Redis 的客戶端和服務端之間採取了一種獨立名為 RESP(REdis Serialization Protocol) 的協議,作者主要考慮了以下幾個點:

  • 容易實現
  • 解析快
  • 人類可讀

注意:RESP 雖然是為 Redis 設計的,但是同樣也可以用於其他 C/S 的軟體。

資料型別及示例

RESP 主要可以序列化以下幾種型別:整數,單行回覆(簡單字串),陣列,錯誤資訊,多行字串。Redis 客戶端向服務端傳送的是一組由執行的命令組成的字串陣列,服務端根據不同的命令回覆不同型別的資料,但協議的每部分都是以 "\r\n" (CRLF) 結尾的。另外 RESP 是二進位制安全的,不需要處理從一個程式到另一個程式的傳輸,因為它使用了字首長度進行傳輸。

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

  • 單行回覆:回覆的第一個位元組是 "+"
  • 錯誤資訊:回覆的第一個位元組是 "-"
  • 整形數字:回覆的第一個位元組是 ":"
  • 多行字串:回覆的第一個位元組是 "$"
  • 陣列:回覆的第一個位元組是 "*"

單行回覆

以 "+" 開頭,以 "\r\n" 結尾的字串形式。e.g.

+OK\r\n複製程式碼

響應的客戶端庫,應該返回除 "+" 和 CRLF 以外的內容,例如上面的內容,則返回 "OK". e.g.

127.0.0.1:6379> set name TaoBeier
+OK\r\n  # 服務端實際返回
---
OK   # redis-cli 客戶端顯示複製程式碼

錯誤資訊

錯誤資訊和單行回覆很像,不過是把 "+" 替換成了 "-"。而這兩者之間真正的區別是,錯誤資訊會被客戶端視為異常,並且組成錯誤型別的是錯誤訊息本身。e.g.

-Error message\r\n複製程式碼

錯誤資訊只在有錯誤發生的時候才會傳送,比如資料型別錯誤,語法錯誤,或者命令不存在之類的。而當接收到錯誤資訊的時候,客戶端庫應該丟擲一個異常。e.g.

127.0.0.1:6379> TaoBeier
-ERR unknown command 'TaoBeier'\r\n  # 服務端實際返回, 下同
---
(error) ERR unknown command 'TaoBeier'  # redis-cli 客戶端顯示, 下同

127.0.0.1:6379> set name TaoBeier moelove
-ERR syntax error\r\n
---
(error) ERR syntax error複製程式碼

整數

這種型別只是只是使用以 ":" 作為字首,以CRLF作為結尾的字串來表示整數。e.g. ":666\r\n" 或者 ":999\r\n" 這種的都是整數回覆。很多命令都會返回整數回覆,例如 INCR LLEN LPUSH 之類的命令。但是多數情況下,返回的整數回覆並沒有過多實際含義,例如 LPUSH 就只是為了表示插入了幾個值,但也有例如 EXISTS 命令是當結果為 true 的時候返回 1,false 返回 0 . e.g.

127.0.0.1:6379> LPUSH info TaoBeier MoeLove
:2\r\n  # 服務端實際返回, 下同
---
(integer) 2  # redis-cli 客戶端顯示, 下同

127.0.0.1:6379> LLEN info
:2\r\n
---
(integer) 2

127.0.0.1:6379> EXISTS info
:1\r\n
---
(integer) 1

127.0.0.1:6379> DEL info
:1\r\n
---
(integer) 1

127.0.0.1:6379> EXISTS info
:0\r\n
---
(integer) 0複製程式碼

多行字串

多行字串被服務端用來返回長度最大為 512MB 的單個二進位制安全的字串。以 "$" 開頭, 後跟實際要傳送的位元組數,隨後是 CRLF,然後是實際的字串資料,最後以 CRLF 結束。所以,例如我們要傳送一個 "moelove.info" 的字串,那它實際就被編碼為 "$12\r\nmoelove.info\r\n"。而如果一個要傳送一個空字串,則會編碼為 "$0\r\n\r\n" 。某些情況下,當要表示不存在的值時候,則以 "$-1\r\n" 返回,這被叫做空多行字串,當客戶端庫接收到這個響應的時候,同樣應該返回一個空值(例如 nil)而不是一個空字串。e.g.

127.0.0.1:6379> set site moelove.info
+OK\r\n  # 服務端實際返回, 下同
---
OK   # redis-cli 客戶端顯示, 下同

127.0.0.1:6379> get site
$12\r\nmoelove.info\r\n
---
"moelove.info"

127.0.0.1:6379> del site
:1\r\n
---
(integer) 1

127.0.0.1:6379> get site
$-1\r\n
---
(nil)

127.0.0.1:6379> set site ''
+OK\r\n
---
OK

127.0.0.1:6379> get site
$0\r\n\r\n
---
""複製程式碼

陣列

陣列型別可用於客戶端向服務端傳送命令,同樣的當某些命令將元素結合返回給客戶端的時候,也是使用陣列型別作為回覆型別的。它以 "*" 開頭,後面跟著返回元素的個數,隨後是 CRLF, 再然後就是陣列中各元素自己的型別了。最典型的是 LRRANGE 命令,返回一個列表中的元素。e.g.

127.0.0.1:6379> LPUSH info TaoBeier moelove.info
:2\r\n   # 服務端實際返回, 下同
---
(integer) 2  # redis-cli 客戶端顯示, 下同

127.0.0.1:6379> LRANGE info 0 -1
*2\r\n$12\r\nmoelove.info\r\n$8\r\nTaoBeier\r\n
---
1) "moelove.info"
2) "TaoBeier"

127.0.0.1:6379> LPOP info
$12\r\nmoelove.info\r\n
---
"moelove.info"

127.0.0.1:6379> LPOP info
$8\r\nTaoBeier\r\n
---
"TaoBeier"

127.0.0.1:6379> LRANGE info 0 -1
*0\r\n
---
(empty list or set)複製程式碼

總結

RESP 協議還是相對易於理解的,另外理解了協議也方便對 Redis 一些問題的定位及客戶端的實現。

參考:


可以通過下面二維碼訂閱我的文章公眾號【MoeLove】

理解 Redis 的 RESP 協議

相關文章