這篇文章會對twemproxyRedis協議解析代碼部分進行一番簡單的分析,同時給出twemproxy目前支持的所有Redis命令。在這篇文章開始前,我想大家去簡單地理解一下有限狀態機,當然不理解也是沒有問題的,有限狀態機僅僅能幫助我們更好地理解twemproxyRedis協議解析代碼部分。 red ...
這篇文章會對twemproxyRedis協議解析代碼部分進行一番簡單的分析,同時給出twemproxy目前支持的所有Redis命令。在這篇文章開始前,我想大家去簡單地理解一下有限狀態機,當然不理解也是沒有問題的,有限狀態機僅僅能幫助我們更好地理解twemproxyRedis協議解析代碼部分。
redis 協議
這邊我們首先需要簡單介紹一下redis協議。參考自https://redis.io/topics/protocol
redis協議即RESP 的數據類型有5類,簡單字元串、錯誤、整數、大字元串以及數組
每一行RESP都以"\r\n" (CRLF)結尾,每一種數據類型都有一個唯一的標識符作為開頭,。
這裡假設 [string(len)]為長度為len的字元長度,[string]為長度為任意的的字元長度,[int]為整數
簡單字元串
這種數據類型往往表示一種正確的信息,其標識符為+,格式為
+[string]\r\n
如對於一個操作類命令操作成功的回覆是
+OK\r\n
錯誤
這種數據類型往往表示一種錯誤的信息, 其標識符為-,格式為
-[string]\r\n
如對於一個操作類命令操作錯誤的回覆可能是
-ERR unknown command 'foobar'\r\n
整數
這種數據類型往往表示一個整數, 其標識符為:,格式為
:[int]\r\n
如對於一些數據類命令的回覆可能是
:1000\r\n
大字元串
這種數據類型往往表示一個有長度len信息的字元串, 其標識符為$,格式為 :
$len\r\n
[string(len)]\r\n
如對於一個命令set的包就是
3\r\n
set\r\n
數組
這種數據類型往往數量為k信息所有類型混合的數據,並不一定要同一類型, 其標識符為*,格式為 :
*k\r\n
k個[簡單字元串、錯誤、整數、大字元串或數組]
如對於一個命令
set skey value
的包就是 :
*3\r\n
$3\r\n
set\r\n
$4\r\n
skey\r\n
$5\r\n
value\r\n
如回覆包
*2\r\n
*3\r\n
:1\r\n
:2\r\n
:3\r\n
*2\r\n
+Foo\r\n
-Bar\r\n
redis請求包解析
在proto/nc_redis.c中的redis_parse_req函數解析了redis請求包
redis請求包有限狀態機的符號圖如下圖所示:
redis請求包符號表轉化圖
寫成正式格式的set skey value一樣的是*3\r\n$3\r\nset\r\n$4\r\nskey\r\n$5\r\nvalue\r\n。對於這個set命令就是以SW_REQ_TYPE(set),SW_KEY(skey ),SW_ARG1(value)組成的,redis命令的基本的組成如下:SW_REQ_TYPE SW_KEY [SW_ARG1] [SW_ARG2] [SW_ARG3] ... [SW_ARGN]([]里的可以出現或者不出現,視SW_REQ_TYPE 的類型所示),SW_KEY 可以是是多個。
如果是只有SW_KEY 的是滿足函數redis_argx的命令,帶有SW_ARG1的是滿足函數redis_arg1以及redis_argkvx的命令,帶有SW_ARG2的是滿足函數redis_arg2的命令,帶有滿足SW_ARG3的是函數redis_arg3的命令,帶有SW_ARGN的是滿足函數redis_argn以及redis_argeval的命令,為此我們可以畫出代碼state之間的轉化關係
1 enum { 2 SW_START, 3 SW_NARG, 4 SW_NARG_LF, 5 SW_REQ_TYPE_LEN, 6 SW_REQ_TYPE_LEN_LF, 7 SW_REQ_TYPE, 8 SW_REQ_TYPE_LF, 9 SW_KEY_LEN, 10 SW_KEY_LEN_LF, 11 SW_KEY, 12 SW_KEY_LF, 13 SW_ARG1_LEN, 14 SW_ARG1_LEN_LF, 15 SW_ARG1, 16 SW_ARG1_LF, 17 SW_ARG2_LEN, 18 SW_ARG2_LEN_LF, 19 SW_ARG2, 20 SW_ARG2_LF, 21 SW_ARG3_LEN, 22 SW_ARG3_LEN_LF, 23 SW_ARG3, 24 SW_ARG3_LF, 25 SW_ARGN_LEN, 26 SW_ARGN_LEN_LF, 27 SW_ARGN, 28 SW_ARGN_LF, 29 SW_SENTINEL 30 } state;View Code
redis請求包狀態轉化圖
通過這種方式twemproxy解析了redis的請求包,首先解析了每個包的類型,然後將每一個key的開始、結束指針記錄到相應的包中,用來完成切片操作。這種有限狀態機的方式不僅比正則表達式解析速度快,而且代碼較為清晰。
redis回覆包解析
在proto/nc_redis.c中的redis_parse_rsp函數解析了redis請求包
這裡用過符號區分了redis協議的回覆包類型,這裡的符號的意思就是指在上面《redis協議》章節中提到的符號
SW_STATUS是簡單字元串
SW_ERROR是錯誤
SW_INTEGER是整數
SW_BULK是大字元串
SW_MULTIBULK是數組
下麵是redis協議的解析狀態:
1 enum { 2 SW_START, 3 SW_STATUS, 4 SW_ERROR, 5 SW_INTEGER, 6 SW_INTEGER_START, 7 SW_SIMPLE, 8 SW_BULK, 9 SW_BULK_LF, 10 SW_BULK_ARG, 11 SW_BULK_ARG_LF, 12 SW_MULTIBULK, 13 SW_MULTIBULK_NARG_LF, 14 SW_MULTIBULK_ARGN_LEN, 15 SW_MULTIBULK_ARGN_LEN_LF, 16 SW_MULTIBULK_ARGN, 17 SW_MULTIBULK_ARGN_LF, 18 SW_RUNTO_CRLF, 19 SW_ALMOST_DONE, 20 SW_SENTINEL 21 } state;View Code
redis回覆包狀態轉化圖
在這幅redis回覆包狀態轉化圖中(每個狀態下麵的條件,就是進入該狀態的條件),通過這些,我們可以解析回覆包。如:
*2\r\n
*2\r\n
+Foo\r\n
-Bar\r\n
對於
*2\r\n
遇到*那麼進入SW_MULTIBULK,接著遇到'\r',
進入SW_MULTIBULK_NARG_LR,繼而進入SW_MULTIBULK_ARGN_LEN
對於
*3\r\n
:1\r\n
:2\r\n
:3\r\n
先遇到*再次進入SW_MULTIBULK,
接著遇到'\r',進入SW_MULTIBULK_NARG_LR,繼而進入SW_MULTIBULK_ARGN_LEN
然後就是進入SW_SLMPLE,最後就是一直在SW_SLMPLE和SW_MULTIBULK_ARGN_LEN狀態轉化,
對於
*2\r\n
+Foo\r\n
-Bar\r\n
也是同樣的,與上面那塊解析過程相同。
當然這裡的命令僅僅是個例子,可能不符合redis協議要求。僅僅能幫助我們更好的理解。
總結
在上述章節中,我們瞭解了redis的協議,以及twemproxy對redis請求包和回覆包的解析過程,利用有限狀態機的模型來去熟悉解析過程,下麵我們會探索msg_resv的過程。
另外,對於博文有問題的請大家在評論中留言與博主討論,博主會及時回覆的!!!!