这篇文章会对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之间的转化关系

    enum {
SW_START,
SW_NARG,
SW_NARG_LF,
SW_REQ_TYPE_LEN,
SW_REQ_TYPE_LEN_LF,
SW_REQ_TYPE,
SW_REQ_TYPE_LF,
SW_KEY_LEN,
SW_KEY_LEN_LF,
SW_KEY,
SW_KEY_LF,
SW_ARG1_LEN,
SW_ARG1_LEN_LF,
SW_ARG1,
SW_ARG1_LF,
SW_ARG2_LEN,
SW_ARG2_LEN_LF,
SW_ARG2,
SW_ARG2_LF,
SW_ARG3_LEN,
SW_ARG3_LEN_LF,
SW_ARG3,
SW_ARG3_LF,
SW_ARGN_LEN,
SW_ARGN_LEN_LF,
SW_ARGN,
SW_ARGN_LF,
SW_SENTINEL
} 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协议的解析状态:

    enum {
SW_START,
SW_STATUS,
SW_ERROR,
SW_INTEGER,
SW_INTEGER_START,
SW_SIMPLE,
SW_BULK,
SW_BULK_LF,
SW_BULK_ARG,
SW_BULK_ARG_LF,
SW_MULTIBULK,
SW_MULTIBULK_NARG_LF,
SW_MULTIBULK_ARGN_LEN,
SW_MULTIBULK_ARGN_LEN_LF,
SW_MULTIBULK_ARGN,
SW_MULTIBULK_ARGN_LF,
SW_RUNTO_CRLF,
SW_ALMOST_DONE,
SW_SENTINEL
} state;

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协议要求。仅仅能帮助我们更好的理解。

补充

关于mbuf的解析过程看完代码之后,msg中的mbuf仅仅是到达合适大小之后的分出新的mbuf,这就是msg_repair的功能

总结

在上述章节中,我们了解了redis的协议,以及twemproxy对redis请求包和回复包的解析过程,利用有限状态机的模型来去熟悉解析过程,下面我们会探索msg_send的过程。

另外,对于博文有问题的请大家在评论中留言与博主讨论,博主会及时回复的!!!!

twemproxyRedis协议解析探索——剖析twemproxy代码正编的更多相关文章

  1. twemproxyMemcache协议解析探索——剖析twemproxy代码正编补充

    memcache是一种和redis类似的高速缓存服务器,但是memcache只提供键值对这种简单的存储方式,相对于redis支持的存储方式多样化,memcache就比较简单了.memcache通过tc ...

  2. twemproxy发送流程探索——剖析twemproxy代码正编

    本文想要完成对twemproxy发送流程--msg_send的探索,对于twemproxy发送流程的数据结构已经在<twemproxy接收流程探索--剖析twemproxy代码正编>介绍过 ...

  3. twemproxy接收流程探索——剖析twemproxy代码正编

    本文旨在帮助大家探索出twemproxy接收流程的代码逻辑框架,有些具体的实现需要我们在未来抽空去探索或者大家自行探索.在这篇文章开始前,大家要做好一个小小的心理准备,由于twemproxy代码是一份 ...

  4. twemproxy代理主干流程——剖析twemproxy代码正编

    在twemproxy的发送和接收流程剖析中,我们已经完全弄清楚twemproxy如何将客户端以及服务端发来的包切分成msg,获得一个独立的msg后twemproxy应该如何处理?这是本文这次需要重点介 ...

  5. twemproxy分片处理原理--剖析twemproxy代码正编

    twemproxy在redis上能处理多命令流程只有mset,mget,del的命令,例如mset的话是mset k1 v1 k2 v2 k3 k3,mget的话是mget k1 k2 k3,del的 ...

  6. twemproxy代码框架概述——剖析twemproxy代码前编

    本篇将去探索twemproxy源码的主干流程,想来对于想要开始啃这份优秀源码生肉的童鞋会有不小的帮助.这里我们首先要找到 twemproxy正确的打开方式--twemproxy的文件结构,接着介绍tw ...

  7. twemproxy架构分析——剖析twemproxy代码前编

    twemproxy背景 在业务量剧增的今天,单台高速缓存服务器已经无法满足业务的需求, 而相较于大容量SSD数据存储方案,缓存具备速度和成本优势,但也存在数据安全性的挑战.为此搭建一个高速缓存服务器集 ...

  8. twemproxy接收流程探索——twemproxy代码分析正编

    在这篇文章开始前,大家要做好一个小小的心理准备,由于twemproxy代码是一份优秀的c语言,为此,在twemproxy的代码中会大篇幅使用c指针.但是不论是普通类型的指针还是函数指针,都可以让我们这 ...

  9. 剖析twemproxy前言

    又是喜闻乐见的新坑,前面的mysql协议,当我在解读go-mysql包的时候,会重新讲到,至于Leetcode的更新会与go语言同步.关于这个redis的新坑,目前打算通过剖析twemproxy源码来 ...

随机推荐

  1. php中var_dump() 打印出一个对象的时候,信息怎么看?

    php 的一个依赖注入容器, 说白了,就是用php 的反射类,来在运行的时候动态的分析类具有的函数,以及动态分析函数的参数, 从而实例化类,并执行类的方法. 另外,php 中的 typehint 还是 ...

  2. implemented loader.php

    http://stackoverflow.com/questions/11787176/manage-url-routes-in-own-php-framework This is how i imp ...

  3. Ibatis 3.0 之前使用的都是2.0 3.0与2.0的内容有很大的不同

    以前用过ibatis2,但是听说ibatis3有较大的性能提升,而且设计也更合理,他不兼容ibatis2.尽管ibatis3还是beta10的状态,但还是打算直接使用ibatis3.0, ibatis ...

  4. CodeForces 660B Seating On Bus

    模拟. #include<cstdio> #include<cstring> #include<cmath> #include<vector> #inc ...

  5. seajs的常用api简易文档

    目前使用sea.js的公司越来越多, 比如朋友网,阿里巴巴,淘宝网,百姓网,支付宝,有道云笔记等.模块化的javascript开发带来了可维护,可扩展性,尤其在多人协作开发的时候不用再担心文件依赖和函 ...

  6. LED调光,PFM即pulse frequence modulation

    PWM不是唯一的调制方式,可以PWM,也可以PFM,也可以混合调制. PWM即pulse width modulation的缩写,脉冲宽度调制,保持开关周期不变,调节开关导通时间. PFM即pulse ...

  7. URL中有中文字符,转码方法

    服务端返回的urlString里面有时含有中文,使用 [NSURL URLWithString:urlString]生成URL对象时,iOS客户端不能正确进行网络请求,网上找到的URLEncode方法 ...

  8. SVN简单流程---以公司的使用方法为例

    一  名词解释 svn一般包括      branch  每个branch版本都要生成对应的tag.例如 branch版本号为1.0.0,那么提交后生成的tag版本号为1.0.0.0;当branch修 ...

  9. 《javascript语言精粹》——第6章数组

    [1].数组字面量 var empty=[]; var num=[ 'zero','one','two','three','four','five','six','seven','eight','ni ...

  10. STM8的GPIO驱动

    芯片的外设一般按照这么几个流程来进行,GPIO,外部中断,定时器,串口,ADC,IIC,SPI,下面我就按照各个模式来写 首先是GPIO,STM8的GPIO拥有复用功能,这句话告诉我们必然需要配置IO ...