标题:
深入浅出 Redis client/server交互流程(1)
[打印本页]
作者:
look_w
时间:
2018-12-18 20:44
标题:
深入浅出 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方法主要做以下几件事:
执行一次快速的主动过期检查,检查是否有过期的key
当有客户端阻塞时,向所有从库发送ACK请求
unblock 在同步复制时候被阻塞的客户端
尝试执行之前被阻塞客户端的命令
将AOF缓冲区的内容写入到AOF文件中
如果是集群,将会根据需要执行故障迁移、更新节点状态、保存node.conf 配置文件。
如此,redis整个事件管理器机制就比较清楚了。接下来进一步探讨并理解事件是如何触发并创建。
交互的六大步骤下面正式开始介绍redis client/server 交互的6大步骤
一、Client 发起socket 连接(点击放大图像)
这里以redis-cli 客户端为例,当执行以下语句时:
[root@zbdba redis-3.0]
#
./src/redis-cli -p 6379 -h 127.0.0.1
127.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.sock
redis /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