<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
redis是一個非常優秀的軟體,它可以用作記憶體資料庫或者快取。因為他的優秀效能,redis被應用在很多場合中。
redis是一個使用者端和伺服器端的模式,使用者端和伺服器端是通過TCP協定進行連線的,使用者端將請求資料傳送到伺服器端,伺服器端將請求返回給使用者端。這樣一個請求流程就完成了。
當然在最開始的時候,因為用的人很少,系統還不夠穩定,通過TCP協定傳輸的資料不規範的。但是當用的人越來越多,尤其是希望開發適用於不同語言和平臺的redis使用者端的時候,就要考慮到相容性的問題了。
這時候使用者端和伺服器端就需要一個統一的互動協定,對於redis來說這個通用的互動協定就叫做Redis serialization protocol(RESP)。
RESP是在Redis 1.2版本中引入的,並在Redis 2.0中成為了與 Redis 伺服器通訊的標準方式。
這就是說,從Redis 2.0之後,就可以基於redis protocol協定開發出自己的redis使用者端了。
一般來說,redis的使用者端和伺服器端組成的是一個請求-響應的模式,也就是說使用者端向伺服器端傳送請求,然後得到伺服器端的響應結果。
請求和響應是redis中最簡單的用法。熟悉redis的朋友可能會想到了兩個redis的高階用法,這兩個用法並不是傳統意義上的請求-響應模式。
到底是哪兩種用法呢?
第一種就是redis支援pipline,也就是管道操作,管道的好處就是redis使用者端可以一次性向伺服器端傳送多條命令,然後等待伺服器端的返回。
第二種redis還支援Pub/Sub,也就是廣播模型,在這一種情況下,就不是請求和響應的模式了,在Pub/Sub下,切換成了伺服器端推播的模式。
為什麼要用pipline呢?
因為redis是一個典型的請求響應模式,我們來舉個常見的incr命令的例子:
Client: INCR X Server: 1 Client: INCR X Server: 2 Client: INCR X Server: 3 Client: INCR X Server: 4
事實上使用者端只想得到最終的結果,但是每次使用者端都需要等待伺服器端返回結果之後,才能傳送下一次的命令。這樣就會導致一個叫做RTT(Round Trip Time)的時間浪費。
雖然每次RTT的時間不長,但是累計起來也是一個非常客觀的數位。
那麼可不可以將所有的使用者端命令放在一起傳送給伺服器呢? 這個優化就叫做Pipeline。
piepline的意思就是使用者端可以在沒有收到伺服器端返回的時候繼續向伺服器端傳送命令。
上面的命令可以用pipline進行如下改寫:
(printf "INCR XrnINCR XrnINCR XrnINCR Xrn"; sleep 1) | nc localhost 6379 :1 :2 :3 :4
因為redis伺服器支援TCP協定進行連線,所以我們可以直接用nc連到redis伺服器中執行命令。
在使用pipline的時候有一點要注意,因為redis伺服器會將請求的結果快取在伺服器端,等到pipline中的所有命令都執行完畢之後再統一返回,所以如果伺服器端返回的資料比較多的情況下,需要考慮記憶體佔用的問題。
那麼pipline僅僅是為了減少RTT嗎?
熟悉作業系統的朋友可能有聽說過使用者空間和作業系統空間的概念,從使用者輸入讀取資料然後再寫入到系統空間中,這裡涉及到了一個使用者空間的切換,在IO操作中,這種空間切換或者拷貝是比較耗時的,如果頻繁的進行請求和響應,就會造成這種頻繁的空間切換,從而降低了系統的效率。
使用pipline可以一次性傳送多條指令,從而有效避免空間的切換行為。
和Pub/Sub相關的命令是SUBSCRIBE, UNSUBSCRIBE 和 PUBLISH。
為什麼要用Pub/Sub呢?其主要的目的就是解耦,在Pub/Sub中訊息傳送方不需要知道具體的接收方的地址,同樣的對於訊息接收方來說,也不需要知道具體的訊息傳送方的地址。他們只需要知道關聯的主題即可。
subscribe和publish的命令比較簡單,我們舉一個例子,首先是使用者端subscribe topic:
redis-cli -h 127.0.0.1 127.0.0.1:6379> subscribe topic Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "topic" 3) (integer) 1
然後在另外一個終端,呼叫publish命令:
redis-cli -h 127.0.0.1 127.0.0.1:6379> publish topic "what is your name?" (integer) 1
可以看到使用者端會收到下面的訊息:
1) "message" 2) "topic" 3) "what is your name?"
RESP協定有5種型別,分別是imple Strings, Errors, Integers, Bulk Strings 和 Arrays。
不同的型別以訊息中的第一個byte進行區分,如下所示:
型別 | 第一個byte |
---|---|
Simple Strings | + |
Errors | - |
Integers | : |
Bulk Strings | $ |
Arrays | * |
protocol中不同的部分以 "rn" (CRLF)來進行區別。
Simple Strings的意思是簡單的字串。
通常用在伺服器端的返回中,這種訊息的格式就是"+"加上文字訊息,最後以"rn"結尾。
比如伺服器端返回OK,那麼對應的訊息就是:
"+OKrn"
上面的訊息是一個非二進位制安全的訊息,如果想要傳送二進位制安全的訊息,則可以使用Bulk Strings。
什麼是非二進位制安全的訊息呢?對於Simple Strings來說,因為訊息是以"rn"結尾,所以訊息中間不能包含"rn"這兩個特殊字元,否則就會產生錯誤的含義。
Bulk Strings是二進位制安全的。這是因為Bulk Strings包含了一個字元長度欄位,因為是根據長度來判斷字元長度的,所以並不存在根據字元中某個特定字元來判斷是否字元結束的缺點。
具體而言Bulk Strings的結構是"$"+字串長度+"rn"+字串+"rn"。
以OK為例,如果以Bulk Strings來表示,則如下所示:
"$2rnokrn"
Bulk Strings還可以包含空字串:
"$0rnrn"
當然還可以表示不存在的Null值:
"$-1rn"
這是redis中的整數表示,具體的格式是":"+整數+"rn"。
比如18這個整數就可以用下面的格式來表示:
":18rn"
redis的多個命令可以以array來表示,伺服器端返回的多個值也可以用arrays來表示。
RESP Arrays的格式是"*"+陣列中的元素個數+其他類似的資料。
所以RESP Arrays是一個複合結構的資料。比如一個陣列中包含了兩個Bulk Strings:"redis","server"則可以用下面的格式來表示:
"*2rn$5rnredisrn$6rnserverrn"
RESP Arrays中的原始不僅可以使用不同型別,還能包含RESP Arrays,也就是array的巢狀:
"*3rn$5rnredisrn$6rnserverrn*1rn$4rngoodrn"
為了方便觀察,我們將上面的訊息格式一下:
"*3rn $5rnredisrn $6rnserverrn *1rn $4rngoodrn"
上面的訊息是一個包含三個元素的陣列,前面兩個元素是Bulk Strings,最後一個是包含一個元素的陣列。
最後,RESP還可以表示錯誤訊息。RESP Errors的訊息格式是"-"+字串,如下所示:
"-Err something wrongrn"
一般情況下,"-"後面的第一個單詞表示的是錯誤型別,但是這只是一個約定俗成的規定,並不是RESP協定中的強制要求。
另外,經過對比,大家可能會發現RESP Errors和Simple Strings是訊息格式是差不多的。
這種對不同訊息型別的處理是在使用者端進行區分的。
如果完全按RESP協定的要求,當我們連線到伺服器端的時候需要包含RESP中定義訊息的所有格式,但是這些訊息中包含了額外的訊息型別和回車換行符,所以直接使用協定來執行的話會比較困惑。
於是redis還提供一些內聯的命令,也就是協定命令的精簡版本,這個精簡版本去除了訊息型別和回車換行符。
我們以"get world"這個命令為例。來看下不同方式的連線情況。
首先是使用redis-cli進行連線:
redis-cli -h 127.0.0.1 127.0.0.1:6379> get world "hello"
因為redis-cli是redis的使用者端,所以可以直接使用inline command來執行命令。
如果使用telnet,我們也可以使用同樣的命令來獲得結果:
telnet 127.0.0.1 6379 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. get world $5 hello
可以看到返回的結果是"$5rnhellorn"。
如果要使用協定訊息來請求redis伺服器應該怎麼做呢?
我們要請求的命令是"get world",將其轉換成為RESP的訊息則是:
"*2rn$3rngetrn$5rnworldrn"
我們嘗試一下將上述命令使用nc傳遞到redis server上:
(printf "*2rn$3rngetrn$5rnworldrn"; sleep 1) | nc localhost 6379 -ERR Protocol error: expected '$', got ' '
很遺憾我們得到了ERR,那麼是不是不能直接使用RESP訊息格式進行傳輸呢?當然不是,上面的問題在於$
符號是一個特殊字元,我們需要跳脫一下:
(printf "*2rn$3rngetrn$5rnworldrn"; sleep 1) | nc localhost 6379 $5 hello
可以看到輸出的結果和直接使用redis-cli一致。
以上就是RESP協定的基本內容和手動使用的例子,有了RESP,我們就可以根據協定中定義的格式來建立redis使用者端。
可能大家又會問了,為什麼只是redis使用者端呢?有了協定是不是redis伺服器端也可以建立呢?答案當然是肯定的,只需要按照協定進行訊息傳輸即可。主要的問題在於redis伺服器端的實現比較複雜,不是那麼容易實現的。
以上就是redis protocol通訊協定及使用詳解的詳細內容,更多關於redis protocol通訊協定的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45