網站首頁 編程語言 正文
引言
我們之前已經學習了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
長度的數據(不包含\r\n
),所以我們可以這樣來寫偽代碼。
根據如上,我們很容易寫出偽代碼。
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
長度的數據即可,這里主要要處理一下\r\n
。
將命令構建RESP
報文規范,根據拆解反操作就可以了,這里暫不介紹了。
上述,我們核心功能已經探討完畢了。
功能實現
代碼已經編寫完畢,放置在了gitee
上: gitee
如上我們已經學會了如何拆解和組裝RESP
協議了,我們接著來看,我們如何用go
來編寫拆解和組裝協議的代碼呢? 我們可以看。
我們先創建一個字符,然后將其封裝為bufio.Reader
,我們來看下:
因為我們要使用readLine()
函數,所以我們需要將其轉換為bufio.Reader
類型,若是直接從net.Conn
中獲取,不用轉換,直接可以使用 bufio.Reader
的。
我們將上述偽代碼編寫一下,實現拆解的功能。
其具體執行過程是我們先獲取一行數據,放置到data
中,而后判斷其前綴是什么,若是*
則取其后面的數據,將其轉為int
類型n
,而后再遞歸該函數n
次,而后中遇到$
,我們則取后面的數據,也是將其轉為int
類型m
,而后再取m
長度的實際數據,這就是我們的命令了,最后我們再踢掉命令的\r\n
即可。
其中,有一個函數是byteToInt
是我們自己寫的通過切片轉為數字的函數,我們看下
該函數主要的功能是將其[]byte
數字轉換為int
數據。
如上,我們整個RESP
協議功能寫完了,我們運行下看下實際效果:
很顯然,我們成功拆解了該數據。
總結
這篇文章,我們介紹了應該如何使用go
簡單的拆解RESP
協議的內容,為什么我們不介紹如何編寫redis
主從中間件呢?
最開始是打算這樣寫的,但是知識多了,介紹起來會很雜,很難把一個點講清楚,所以我們就單獨挑了一個核心點來介紹,我愿意將其稱之為面向核心編程(我的基友很早之前告訴我的),所謂的面向核心編程簡而易在就是我們在涉及一個功能的時候,要學會拆解該功能,將核心功能先用demo
做出來,而后再慢慢豐富周邊,從而完成整個需求涉及。
最后我們再來聊聊RESP
協議,官網在介紹時將其概括為實現簡單、快速解析、直接可閱讀。如果你認真學習這2篇文章,肯定對此深有感悟。
原文鏈接:https://juejin.cn/post/7138058006482124837
相關推薦
- 2022-04-02 Android?studio實現日期?、時間選擇器與進度條_Android
- 2022-05-09 React中的axios模塊及使用方法_React
- 2022-05-13 CLion 中文輸出亂碼
- 2022-08-23 python實現GATK多線程加速示例_python
- 2022-08-03 python庫Tsmoothie模塊數據平滑化異常點抓取_python
- 2022-10-23 C#使用Lazy<T>實現對客戶訂單的延遲加載_C#教程
- 2023-02-27 nginx編譯安裝及常用參數詳解_nginx
- 2022-07-31 C語言數據結構算法基礎之循環隊列示例_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支