為什么自己做的網(wǎng)站打開是亂碼百度seo公司整站優(yōu)化
簡述
Redis 使用 RESP 協(xié)議(Redis Serialzation Protocol)協(xié)議定義了客戶端和服務(wù)器端交互的命令、數(shù)據(jù)的編碼格式。在 Redis 2.0 版本中,RESP 協(xié)議正式稱為客戶端和服務(wù)器端的標準通信協(xié)議。從 Redis 2.0 到 Redis 5.0 ,RESP 協(xié)議都稱為 RESP 2 協(xié)議,從 Redis 6.0 開始,Redis 就采用 RESP 3 協(xié)議了。
1.客戶端和服務(wù)器端交互的內(nèi)容有哪些?
RESP 2 協(xié)議
是如何對命令和數(shù)據(jù)進行格式編碼的呢?
我們可以把交互內(nèi)容,分成客戶端請求和服務(wù)器端響應(yīng)兩類:
- 在客戶端請求中,客戶端會給 Redis 發(fā)送命令,以及要寫入的鍵和值。
- 而在服務(wù)器響應(yīng)中,Redis 實例會返回:
- 讀取的值
- OK 標識
- 成功寫入的元素個數(shù)
- 錯誤信息
- 以及命令(如 Redis Cluster 中的 MOVE 命令)
其實,這些交互內(nèi)容還可以再進一步細分成 7 類:
- 命令:這就是針對不同數(shù)據(jù)類型的
命令操作
。例如對 String 類型的 SET、GET 操作,對 Hash 類型的 HSET、HGET 等,這些命令就是代表操作語義的字符串。 - 鍵:鍵值對中的鍵,可以直接用
字符串
表示。 - 單個值:對應(yīng)
String 類型
的數(shù)據(jù),數(shù)據(jù)本身可以是字符串、數(shù)值(整數(shù)或浮點數(shù)),布爾值(True 或 False)等。 - 集合值:對應(yīng) List、Hash、Set、Sorted Set 類型的數(shù)據(jù),不僅包含多個值,而且每個值也可以是字符串、數(shù)值或布爾值等。
- OK 回復:對應(yīng)命令操作成功的結(jié)果,就是一個字符串的
OK
。 - 整數(shù)回復:這里有兩種情況。
- 一種是
命令操作返回的結(jié)果是整數(shù)
,例如 LLEN 命令返回列表的長度。 - 另一種是集合命令成功操作時,
實際操作元素的個數(shù)
,例如 SADD 命令返回成功添加的元素個數(shù)。
- 一種是
- 錯誤信息:命令操作出錯是的返回結(jié)果,包括
error
標識,以及具體的錯誤信息。
下面再接個三個具體的例子,幫助你進一步掌握這些交互內(nèi)容。
第一個例子
# 成功寫入String類型數(shù)據(jù),返回 OK
127.0.0.1:6379> SET testkey testvalue
OK
這里的交互內(nèi)容就包括了命令(SET 命令)、鍵(String類型的鍵 testkey)和單個值(String 類型的 testvalue),而服務(wù)器端直接返回一個 OK 回復。
第二個例子
# 成功寫入Hash類型數(shù)據(jù),返回實際寫入的集合元素個數(shù)
127.0.0.1:6379> HSET testhash a 1 b 2 c 3
(integer) 3
這里的交互內(nèi)容包括三個 key-value 的 Hash 集合值(a 1 b 2 c 3),而服務(wù)器端返回整數(shù)回復(3),表示成功寫入的元素個數(shù)。
第三個例子
# 發(fā)送的命令不對,報錯,并返回報錯信息
127.0.0.1:6379> PUT testkey2 testvalue
(error) ERR unknown command 'PUT', with args beginning with: 'testkey2', 'testvalue'
這里的交互內(nèi)容包括三個 key-value 的 Hash 集合值(a 1 b 2 c 3),而服務(wù)器端返回整數(shù)回復(3),表示成功寫入的元素個數(shù)。
可以看到,這里的交互內(nèi)容包括錯誤信息,這是因為,Redis 實例本身不支持 PUT 命令,所以服務(wù)器報錯 error
,并返回具體的報錯,也就是位置的命令 “PUT”。
2.RESP 2的編碼格式規(guī)范
RESP 2 協(xié)議的設(shè)計目標是,希望 Redis 開發(fā)人員實現(xiàn)客戶端時簡單方便,這樣可以減少客戶端開發(fā)時出現(xiàn)的 Bug。而且,客戶端和服務(wù)器端交互出現(xiàn)問題時,開發(fā)人員可以通過查看協(xié)議交互過程,能快速定位問題,方便調(diào)試。所以,RESP 2 協(xié)議采用了可讀性很好的文本形式進行編碼,也就是通過字符串,來表示各種命令和數(shù)據(jù)。
不過交互內(nèi)容有多重,而且,實際傳輸?shù)拿罨驍?shù)據(jù)也會有多個。針對這兩種情況,RESP 2 協(xié)議在編碼時設(shè)計了兩個基本規(guī)范。
- 為了對不同類型的交互內(nèi)容進行編碼,RESP 2 協(xié)議實現(xiàn)了物資編碼格式類型。同時,為了區(qū)分這 5 種編碼類型,RESP 2 使用一個專門的字符,作為每種編碼類型的開頭字符。這樣,客戶端或服務(wù)器端進行解析時,可以直接通過開頭字符串知道當前解析的編碼類型。
- RESP 2 進行編碼時,會按照單個命令或單個數(shù)據(jù)的粒度進行編碼,并在每個編碼結(jié)果后增加一個換行符
\r\n
(有時也表示成 CRLF),表示一次編碼結(jié)束。
接下來,分別結(jié)束下這 5 中編碼類型。
2.1 簡單字符串類型(RESP Simple String)
這種類型就是用一個字符串來進行編碼,比如,請求操作在服務(wù)器端成功執(zhí)行后的 OK 表示回復,就是這種類型進行編碼的
當服務(wù)器端成功執(zhí)行一個操作后,返回的 OK 標識就可以編碼如下:
+OK\r\n
2.2 長字符串類型(RESP Bulk String)
這種類型是用一個二進制安全的字符串
來進行編碼。
這里的二進制安全,是相對于 C 語言中對字符串的處理方式來說的。 Redis 在解析字符串時,不會像 C 語言那樣,使用 \0
來判定一個字符串的結(jié)尾,Redis 會把 \0
解析成正常的 0 字符,并使用額外的屬性值表示字符串的長度。
對于 Redis\0Cluster\0
這個字符串來說,C 語言會解析為 “Redis”,而 Redis 會解析為 “Redis Cluster”,并用 len 屬性字符串的真實長度是 14 字節(jié),如下圖所示:
這樣一來,字符串中即使存儲了 \0
字符,也不會導致 Redis 解析到 \0
時,就認為字符串結(jié)束了從而停止,這就保證了數(shù)據(jù)的安全性。和長字符串類相比,簡單字符串就是非二進制安全的。
長字符串類型最大可以達到 512 MB,所以可以對很大的數(shù)據(jù)量進行編碼,正好可以滿足鍵值對本身的數(shù)據(jù)量需求,所以 RESP 2 就用這種類型對交互內(nèi)容中的鍵或值進行編碼,并且使用 $
字符作為開頭字符, $
字符后面會緊跟一個數(shù)字,這個數(shù)字表示字符串的實際長度。
例如,我們使用 GET 命令讀取一個鍵(假設(shè)為 testkey)的值(假設(shè)值為 testvalue)時,服務(wù)器端返回的 String 值編碼如下,其中 $
字符后的 9 ,表示數(shù)據(jù)長度為 9 個字符。
$9 testvalue\r\n
2.3 整數(shù)類型(RESP Integer)
這種數(shù)據(jù)類型也是一個字符串,但表示的是一個有符號 64 位整數(shù)。為了和包含數(shù)字的簡答字符串類型區(qū)分開,整數(shù)類型使用 :
字符作為開頭,可以用于對服務(wù)器端返回的整數(shù)回復進行編碼。
例如,上面的第二個例子中,我們使用 HSET 命令設(shè)置了 testhash 的三個元素,服務(wù)器端實際返回的編碼結(jié)果如下:
:3\r\n
2.4 錯誤類型(RESP Errors)
它是一個字符串,包括了錯誤類型和具體的錯誤信息。Redis 服務(wù)器端報錯響應(yīng)就是用這種類型進行編碼的。RESP 2 使用 -
字符作為它的開頭字符。
例如,上面的第三個例子中,我們在 redis-cli 執(zhí)行 PUT testkey2 testvalue
命令報錯,服務(wù)器端實際返回給客戶端的報錯編碼結(jié)果如下:
-ERR unknown command 'PUT', with args beginning with: 'testkey2', 'testvalue'
其中 ERR
就是報錯類型,表示一個統(tǒng)一錯誤,ERR 后面的文字內(nèi)容就是具體的報錯信息。
2.5 數(shù)組編碼類型(RESP Arrays)
這是一個包含多個元素的數(shù)組,之前,元素的類型可以是剛剛介紹的 4 種編碼類型。
在客戶端發(fā)送請求和服務(wù)器端返回結(jié)果時,數(shù)組編碼類型都能用的上??蛻舳嗽诎l(fā)送請求操作時,一般會同時包括命令和要操作的數(shù)據(jù)。而數(shù)組類型包含了多個元素,所以,就適合用來對發(fā)送的命令和數(shù)據(jù)進行編碼。為了和其他類型區(qū)分開,RESP 2 使用 *
字符作為它的開頭字符。
例如,我們執(zhí)行 GET testkey
,此時,客戶端發(fā)送給服務(wù)器端的命令的編碼結(jié)果就是使用數(shù)組類型編碼的,如下所示:
*2\r\n$3\r\nGET\r\n$7\r\ntestkey\r\n
- 其中,第一個
*
字符標識當前是數(shù)組類型的編碼結(jié)果,2 標識該數(shù)組有 2 個元素,分別對應(yīng)命令 GET 和 鍵 testkey。 - 命令 GET 和 鍵 testkey,都是使用長字符串類型編碼的,所以用
$
字符加字符串長度來表示。
類型的,當服務(wù)器端返回包含多個元素的集合類型數(shù)據(jù)時,也會用 *
字符和元素個數(shù)作為標識,并用長字符串類型對返回的集合元素進行編碼。
RESP 2 協(xié)議的 5 種編碼類型和相應(yīng)的開頭字符,我在下表做了小結(jié)。
編碼類型 | 簡單字符串 | 長字符串 | 整數(shù) | 錯誤 | 數(shù)組 |
---|---|---|---|---|---|
開頭第一個字符 | + | $ | : | - | * |
3.RESP 2 的不足和 RESP 3 的改進
雖然 RESP 2 協(xié)議提供了 5 種編碼類型,看起來很豐富,其實是不夠的。比較,基本數(shù)據(jù)類型還包括很多,例如浮點數(shù)、布爾值等。編碼類型偏少,會帶來兩個問題。
- 一方面,在值的基本數(shù)據(jù)類型方面,RESP 2 只能區(qū)分字符串和證書,對于其他的數(shù)據(jù)類型,客戶端使用 RESP 2 協(xié)議時,就需要進行額外的轉(zhuǎn)換操作。例如,當一個浮點數(shù)用字符串表示時,客戶端需要將字符串中的值和實際數(shù)字值比較,判斷是否為數(shù)字值,然后再將字符串轉(zhuǎn)換成實際的浮點數(shù)。
- 另一方面,RESP 2 用數(shù)組類別編碼表示所有的集合類型,但是,Redis 的集合類型包括了 List、Hash、Set 和 Sorted Set。當客戶端接收到數(shù)組類型編碼的結(jié)果時,還需要根據(jù) 調(diào)用的命令操作接口,來判斷返回的數(shù)組究竟是哪一種集合類型。
假設(shè)一個 Hash 類型的鍵是 testhash,集合元素分別為 a:1、b:2、c:3。同時,有一個 Sorted Set 類型的鍵 testzset,集合的元素分別是 a、b、c,它們的分數(shù)分別是 1、2、3.我們在 redis-cli 客戶端中讀取它們返回的結(jié)果時,返回的形式都是一個數(shù)組:
127.0.0.1:6379> HGETALL testhash
1) "a"
2) "1"
3) "b"
4) "2"
5) "c"
6) "3"127.0.0.1:6379> ZRANGE testzset 0 3 withscores
1) "a"
2) "1"
3) "b"
4) "2"
5) "c"
6) "3"
為了在客戶端按照 Hash 和 Sorted Set 兩種類型處理代碼中返回的數(shù)據(jù),客戶端還需要根據(jù)發(fā)送的命令操作 HGETALL 和 ZRANGE ,來把這兩個編碼的數(shù)組結(jié)果轉(zhuǎn)換成相應(yīng)的 Hash 集合和有序集合,增加了客戶端額外的開銷。
從 Redis 6.0 版本開始,RESP 3 協(xié)議增加了對多種數(shù)據(jù)類型的支持,包括:
- 空值
- 浮點數(shù)
- 布爾值
- 有序的字典集合
- 無需的集合
- 等
RESP 3 也是通過不同的開頭字符區(qū)分不同的數(shù)據(jù)類型,例如,當開頭第一個字符是 ,
,就表示接下來的結(jié)果是浮點數(shù)。這樣一來,客戶端就不用再通過額外的字符串對比,來實現(xiàn)數(shù)據(jù)轉(zhuǎn)換操作了,提升了客戶端的效率。
4.小結(jié)
RESP 2 協(xié)議題了 5 中類型的編碼格式,包括簡單字符串類型、長字符串類型、整數(shù)類型、錯誤類型和數(shù)組類型。為了區(qū)分這 5 種類型,RESP 2 協(xié)議使用了 5 種不同的開頭字符作為這 5 種類型編碼結(jié)果的第一個字符,分別是 +
、$
、:
、-
、*
。
RESP 2 協(xié)議是文本形式的協(xié)議,實現(xiàn)簡單,可以減少客戶端開發(fā)出現(xiàn)的 Bug,而且可讀性強,便于開發(fā)調(diào)試。當你需要定制化 Redis 客戶端時,就需要了解和掌握 RESP 2 協(xié)議。
RESP 2 協(xié)議的一個不足就是支持的類型偏少,所以 Redis 6.0 版本使用了 RESP 3 協(xié)議。和 RESP 2 協(xié)議相比,RESP 3 協(xié)議增加了對浮點數(shù)、布爾類型、有序字典集合、無序集合等多種類型數(shù)據(jù)的支持。不過,這里,有個地方需要你注意,Redis 6.0 只支持 RESP 3,對 RESP 2 協(xié)議不兼容,所以如果你使用 Redis 6.0 版本,需要確認客戶端已經(jīng)支持了 RESP 3 協(xié)議,否則將無法使用 Redis 6.0。
如果你想查看服務(wù)器端返回數(shù)據(jù)的 RESP 2 編碼結(jié)果,就可以使用 telnet 命令和 Redis 實例連接,執(zhí)行如下命令:
telnet 實例IP 實例端口
接著,給實例發(fā)送命令,這樣就能看到 RESP 2 協(xié)議編碼后的返回結(jié)果了。當然,你也可以在 telnet 中,想 Redis 實例發(fā)送 RESP 2 協(xié)議編寫的命令,實例同樣能處理。