首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

深入浅出 Redis client/server交互流程(1)

深入浅出 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.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选择,这里不着重讨论。
返回列表