Board logo

标题: 深入浅出 Redis client/server交互流程(1) [打印本页]

作者: look_w    时间: 2018-12-24 16:45     标题: 深入浅出 Redis client/server交互流程(1)

综述最近笔者阅读并研究redis源码,在redis客户端与服务器端交互这个内容点上,需要参考网上一些文章,但是遗憾的是发现大部分文章都断断续续的非系统性的,不能给读者此交互流程的整体把握。所以这里我尝试,站在源码的角度,将redis client/server 交互流程尽可能简单地展现给大家,同时也站在DBA的角度给出一些日常工作中注意事项。
Redis client/server 交互步骤分为以下6个步骤:
一、Client 发起socket 连接
二、Server 接受socket连接
三、客户端 开始写入
四、server 端接收写入
五、server 返回写入结果
六、Client收到返回结果
注:为使文章尽可能简洁,这里只讨论客户端命令写入的过程,不讨论客户端命令读取的流程。
在进一步阅读和了解互动流程之前,请大家确保已经熟练掌握了 Linux Socket 建立流程和epoll I/O 多路复用技术 两个技术点,这对文章内容的理解至关重要。
交互的整体流程在介绍6个步骤之前,首先看一下redis client/server 交互流程整体的程序执行流程图:
(点击放大图像)

上图中6个步骤分别用不同的颜色箭头表示,并且最终结果也用相对应的颜色标识。
首先看看绿色框里面的循环执行的方法,最末是epoll_wait方法,即等待事件产生的方法。然后再看第2、4、5步骤的末尾都有epoll_ctl方法,即epoll事件注册函数。关于epoll的相关技术解析请参看文末一段。
在这里的循环还有个beforeSleep方法,其实它跟我们这次讨论的话题没有太大的关系。但是还是想给大家介绍一下。
beforeSleep方法主要做以下几件事:
如此,redis整个事件管理器机制就比较清楚了。接下来进一步探讨并理解事件是如何触发并创建。
交互的六大步骤下面正式开始介绍redis client/server 交互的6大步骤
一、Client 发起socket 连接(点击放大图像)

这里以redis-cli 客户端为例,当执行以下语句时:
[root@zbdba redis-3.0]# ./src/redis-cli -p 6379 -h 127.0.0.1127.0.0.1:6379>
客户端会做如下操作:
1、获取客户端参数,如端口、ip地址、dbnum、socket等
也就是我们执行./src/redis-cli --help 中列出的参数
2、根据用户指定参数确定客户端处于哪种模式
目前共有:
Latency mode/Slave mode/Get RDB mode/Pipe mode/Find big keys/Stat mode/Scan mode/Intrinsic latency mode
以上8种模式例如:stat 模式
[url=][/url]
[root@zbdba redis-3.0]# ./src/redis-cli -p 6379 -h 127.0.0.1 --stat------- data ------ --------------------- load -------------------- - child -keys       mem      clients blocked requests            connections         1          817.18K  2       0       1 (+0)              2           1          817.18K  2       0       2 (+1)              2           1          817.18K  2       0       3 (+1)              2           1          817.18K  2       0       4 (+1)              2           1          817.18K  2       0       5 (+1)              2           1          817.18K  2       0       6 (+1)              2[url=][/url]

我们这里没有指定,就是默认的模式。
3、进入上图中step1的cliConnect 方法,cliConnect主要包含redisConnect、redisConnectUnix方法。这两个方法分别用于TCP Socket连接以及Unix Socket连接,Unix Socket用于同一主机进程间的通信。我们上面是采用的TCP Socket连接方式也就是我们平常生产环境常用的方式,这里不讨论Unix Socket连接方式,如果要使用Unix Socket连接方式,需要配置unixsocket 参数,并且按照下面方式进行连接:
[root@zbdba redis-3.0]# ./src/redis-cli -s /tmp/redis.sockredis /tmp/redis.sock>
4、进入redisContextInit方法,redisContextInit方法用于创建一个Context结构体保存在内存中,如下:
[url=][/url]
/* Context for a connection to Redis */typedef struct redisContext {    int err; /* Error flags, 0 when there is no error */    char errstr[128]; /* String representation of error when applicable */    int fd;    int flags;    char *obuf; /* Write buffer */    redisReader *reader; /* Protocol reader */} redisContext;[url=][/url]

主要用于保存客户端的一些东西,最重要的就是 write buffer和redisReader,write buffer 用于保存客户端的写入,redisReader用于保存协议解析器的一些状态。
5、进入redisContextConnectTcp 方法,开始获取IP地址和端口用于建立连接,主要方法如下:
s = socket(p->ai_family,p->ai_socktype,p->ai_protocolconnect(s,p->ai_addr,p->ai_addrlen)
到此客户端向服务端发起建立socket连接,并且等待服务器端响应。
当然cliConnect方法中还会调用cliAuth方法用于权限验证、cliSelect用于db选择,这里不着重讨论。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0