首頁 > 軟體

Redis RESP 協定實現範例詳解

2022-09-02 18:03:38

引言

我們之前已經學習了RESP協定的內容,且已經完成了一個最簡單的Redis讀寫分離中介軟體,我們來看拆解下該demo, 看看Redis RESP協定實現起來到底有多簡單。

回顧RESP協定

RESP是基於TCP來實現的Redis通訊協定,該協定是以/r/n(行)進行分割的,協定支援5種型別,具體資訊如下:

型別字首備註
簡單字串+簡單字串以+開頭
錯誤資料-錯誤資料以-開頭
整數:整數以:開頭
複雜字串$複雜字串以$開頭
陣列*陣列以*開頭

即,我們向redis傳送命令:set name pdudo,其實傳送的具體資訊是

*3
$3
set
$4
name
$5
pdudo

而伺服器返回的資訊也是類似的,只不過還需要了解+-,這2個字首分別代表正確訊息和錯誤的訊息。

我們準備2個例子,我們來敲一下

例子1

set name pdudo

例子2

lpush pdudo data1
lpush pdudo data2
lrange pdudo 0 -1

快來動動你的小手指,看能不能根據RESP協定規則,將上述例子命令敲出來。現在你體會到了Redis官網介紹RESP協定時所述的 簡單易讀 可麼?

帶著來敲一下

對於RESP來說,一定要搞清楚協定後,最好能夠手寫協定去執行,再考慮寫程式去實現協定!!!

如何拆解RESP協定

終於到了喜聞樂見的環節了,我們要拆解和組裝協定了。 那我們至少來解決如下3個問題:

  • 該協定是基於TCP流的,我們如何判斷整個命令什麼時候結束?
  • 如何拆解命令?

協定什麼時候結束

一般而言,我們自己在使用TCP傳輸資料,都會在資料開頭定義2個或者4個位元組,用於儲存該資料有多少個位元組,這樣方便檢驗接收,類似於這種情況。

RESP有意思了,它是以/r/n來分割的。最前面會以字首來判斷其型別,例如我們傳送命令,其會用到的字首有*以及$,那麼我們如何來判斷,我們要讀取多少個/r/n呢?

因為上述*代表陣列,即有多少組資料需要處理,圖中為n

$表示複雜字串,即需要獲取m個字元資料,不包含/r/n

如何拆解RESP協定

若要拆解命令,則我們得獲取命令,如上圖所示,報文$m,其實記錄的有m長度的資料(不包含rn),所以我們可以這樣來寫虛擬碼。

根據如上,我們很容易寫出虛擬碼

func toArgs(rd *bufio.Reader) {
	data , _ , _ := rd.ReadLine()
	switch data[0] {
	case '*':
		n := data[1:] // 迴圈n次
		for i:=0;i<n;i++ {
			toArgs(rd)
		}
	case '$':
		m := data[1:] // 獲取m個資料
		// 獲取m長度的資料即可
	}
}

如上我們先獲取字首為*的,繼而獲取其值n,我們則迴圈n次,即可獲取該報文的資料。而字首為$的,我們可以直接獲取該m長度的資料即可,這裡主要要處理一下rn

將命令構建RESP報文規範,根據拆解反操作就可以了,這裡暫不介紹了。

上述,我們核心功能已經探討完畢了。

功能實現

程式碼已經編寫完畢,放置在了gitee上: gitee

如上我們已經學會了如何拆解和組裝RESP協定了,我們接著來看,我們如何用go來編寫拆解和組裝協定的程式碼呢? 我們可以看。

我們先建立一個字元,然後將其封裝為bufio.Reader,我們來看下:

因為我們要使用readLine()函數,所以我們需要將其轉換為bufio.Reader型別,若是直接從net.Conn中獲取,不用轉換,直接可以使用 bufio.Reader的。

我們將上述虛擬碼編寫一下,實現拆解的功能。

其具體執行過程是我們先獲取一行資料,放置到data中,而後判斷其字首是什麼,若是*則取其後面的資料,將其轉為int型別n,而後再遞迴該函數n次,而後中遇到$,我們則取後面的資料,也是將其轉為int型別m,而後再取m長度的實際資料,這就是我們的命令了,最後我們再踢掉命令的rn即可。

其中,有一個函數是byteToInt是我們自己寫的通過切片轉為數位的函數,我們看下

該函數主要的功能是將其[]byte數位轉換為int資料。

如上,我們整個RESP協定功能寫完了,我們執行下看下實際效果:

很顯然,我們成功拆解了該資料。

總結

這篇文章,我們介紹了應該如何使用go簡單的拆解RESP協定的內容,為什麼我們不介紹如何編寫redis主從中介軟體呢?

最開始是打算這樣寫的,但是知識多了,介紹起來會很雜,很難把一個點講清楚,所以我們就單獨挑了一個核心點來介紹,我願意將其稱之為面向核心程式設計(我的基友很早之前告訴我的),所謂的面向核心程式設計簡而易在就是我們在涉及一個功能的時候,要學會拆解該功能,將核心功能先用demo做出來,而後再慢慢豐富周邊,從而完成整個需求涉及。

最後我們再來聊聊RESP協定,官網在介紹時將其概括為實現簡單快速解析直接可閱讀。如果你認真學習這2篇文章,肯定對此深有感悟。

更多關於Redis RESP 協定的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com