twemproxyRedis协议解析探索——剖析twemproxy代码正编
这篇文章会对twemproxyRedis协议解析代码部分进行一番简单的分析,同时给出twemproxy目前支持的所有Redis命令。在这篇文章开始前,我想大家去简单地理解一下有限状态机,当然不理解也是没有问题的,有限状态机仅仅能帮助我们更好地理解twemproxyRedis协议解析代码部分。
redis 协议
这边我们首先需要简单介绍一下redis协议。参考自https://redis.io/topics/protocol
redis协议即RESP 的数据类型有5类,简单字符串、错误、整数、大字符串以及数组
每一行RESP都以" " (CRLF)结尾,每一种数据类型都有一个唯一的标识符作为开头,。
这里假设 [string(len)]为长度为len的字符长度,[string]为长度为任意的的字符长度,[int]为整数
简单字符串
这种数据类型往往表示一种正确的信息,其标识符为+,格式为
+[string]
如对于一个操作类命令操作成功的回复是
+OK
错误
这种数据类型往往表示一种错误的信息, 其标识符为-,格式为
-[string]
如对于一个操作类命令操作错误的回复可能是
-ERR unknown command 'foobar'
整数
这种数据类型往往表示一个整数, 其标识符为:,格式为
:[int]
如对于一些数据类命令的回复可能是
:1000
大字符串
这种数据类型往往表示一个有长度len信息的字符串, 其标识符为$,格式为 :
$len
[string(len)]
如对于一个命令set的包就是
3
set
数组
这种数据类型往往数量为k信息所有类型混合的数据,并不一定要同一类型, 其标识符为*,格式为 :
*k
k个[简单字符串、错误、整数、大字符串或数组]
如对于一个命令
set skey value
的包就是 :
*3
$3
set
$4
skey
$5
value
如回复包
*2
*3
:1
:2
:3
*2
+Foo
-Bar
redis请求包解析
在proto/nc_redis.c中的redis_parse_req函数解析了redis请求包
redis请求包有限状态机的符号图如下图所示:
redis请求包符号表转化图
写成正式格式的set skey value一样的是*3 $3 set $4 skey $5 value 。对于这个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;
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;