跟着大彬读源码 - Redis 2 - 服务器如何响应客户端请求?(上)
上次我们通过问题“启动服务器,程序都干了什么?”,跟着源码,深入了解了 Redis 服务器的启动过程。
既然启动了 Redis 服务器,那我们就要连上 Redis 服务干些事情。这里我们可以通过 redis-cli 测试。
现在客户端和服务器都准备好了,那么Redis 客户端和服务器如何建立连接?服务器又是如何响应客户端的请求呢?
1 连接服务器
客户端和服务器进行通讯,首先应该就是建立连接。接下来,我们来看下 redis-cli 与服务器的连接过程。
还记得我们上次使用 gdb
调试程序的步骤吗?让我们对 redis-cli 再来一次,看看源码的执行步骤。在开始之前,记得在编辑器打开 redis-cli.c
,定位到 main
函数的位置,毕竟 gdb 看代码没有编辑器看着舒服。
debug 步骤如下:
# bash
cd /opt/redis-3.2.13
// 启动 Redis 服务。Ctrl+c 可推出服务器启动页,同时保持服务器运行
./src/redis-server --port 8379 &
// 调试 redis-clli
gdb ./src/redis-cli
# gdb
(gdb) b main
(gdb) r -p 8379
(gdb) layout src
(gdb) focus cmd
执行完上述步骤,我们会进入如下界面:
这时候我们就可以回到编辑器页,看看对 main
函数中哪一行比较感兴趣,就停下来研究研究。到了 2618 行,我们会看到有执行 parseOptions
这个函数,看名字,好像是初始化一些可选项。那就进去看看呗。
1.1 初始化客户端配置
函数执行步骤:main
-> parseOptions
-> main
。
我们会看到,在执行 redis-cli
时携带的参数都是在这个函数中解析,比如我们启动的时候带着的 -p
参数,会在 996 行被解析到,同时赋值给客户端的 hostport 配置项。如下图:
1.2 客户端启动模式
函数执行步骤:main
。
回到 main
函数,会看到后面的代码会出现很多 cliConnect
函数。要注意的是,这里并不表示 redis-cli 会执行多次 cliConnect
函数。实际上,每一个 if
语句块,都代表着客户端的一种连接模式,3.2.13 版本支持以下模式:
- Latency mode:延迟模式。
redis-cli --latency -p 8379
用来测试客户端与服务器连接的延迟。还有--history
和--dist
可选项,用来展示不同的形式。 - Slave mode:模拟从节点模式。
- Get RDB mode:生成并发送 RDB 持久化文件,保存在本地。
- Pipe mode:管道模式。将命令封装成指定数据格式,批量发送给 redis 服务器执行。
- Find big keys:统计 bigkey 的分布。
- Stat mode:统计模式。实时展示服务器的统计信息。
- Scan mode:扫描指定模式的键,相当于 scan 模式。
- LRU test mode:实时测试 LRU 算法的命中情况。
1.3 连接服务器
函数执行步骤:main
-> cliConnect
-> redisConnect
-> redisContextInit
-> redisContextConnectTcp
-> _redisContextConnectTcp
-> cliConnect
。
我们上面没有使用特殊模式启动,因此,我们会看到在 2687 行真正的去调用 cliConnect
函数。跟踪进去,让我们看看究竟是如何和服务器进行连接的。
在 cliConnect
函数中,我们看到,根据 hostsocket
的配置项,会使用不同的连接模式。从名字上,我们大概可以猜出,一个是 TCP Socket 连接,另一个是本机 Unix Socket 连接。
如果想要使用 Unix Socket 连接,只需按格式配置 hostscoket
即可:./src/redis-cli -s /tmp/redis.sock
。
我们这里使用 TCP Scoket 连接,使用 redisConnect
函数建立连接。
不断追踪,我们会看到上面所示的函数执行步骤,在 _redisContextConnectTcp
函数中会看到 getaddrinfo
和 connect
函数的调用,这里就是建立 TCP 连接的地方。
1.4 校验权限及选择数据库
函数执行步骤:cliConnect
-> anetKeepAlive
-> cliAuth
-> cliSelect
-> main
。
回到 cliConnect
函数,如果正常连接上服务器后,还会将我们上面创建的 TCP 连接设置为长连接,然后校验权限,选择连接数据库。
...
/* Set aggressive KEEP_ALIVE socket option in the Redis context socket
* in order to prevent timeouts caused by the execution of long
* commands. At the same time this improves the detection of real
* errors. */
anetKeepAlive(NULL, context->fd, REDIS_CLI_KEEPALIVE_INTERVAL);
/* Do AUTH and select the right DB. */
if (cliAuth() != REDIS_OK)
return REDIS_ERR;
if (cliSelect() != REDIS_OK)
return REDIS_ERR;
...
至此,我们已经跑完客户端与服务器建立连接的全过程。感兴趣的小伙伴可以尝试连接不存在的 IP 或 端口,观察程序抛出异常的时机,熟悉整个连接过程。
客户端与 服务器建立连接后,就可以使用相关命令操作数据库中的 key 了。下面我们以 SET KEY VALUE
命令为例,来看看命令的执行过程。
2 发送命令请求
当用户在客户端键入一个命令请求时,客户端会将这个命令请求按协议格式转换,然后通过连接到服务器的套接字,将转换后的命令请求发送给服务器,如图 3 所示:
因此,对于我们上面的命令请求,客户端会转成:
"*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n"
然后发给服务器。
以上是客户端发送命令给服务器的过程,在下一节中,我们再来认识服务器是如何响应客户端请的。
跟着大彬读源码 - Redis 2 - 服务器如何响应客户端请求?(上)的更多相关文章
- 跟着大彬读源码 - Redis 3 - 服务器如何响应客户端请求?(下)
继续我们上一节的讨论.服务器启动了,客户端也发送命令了.接下来,就要到服务器"表演"的时刻了. 1 服务器处理 服务器读取到命令请求后,会进行一系列的处理. 1.1 读取命令请求 ...
- 跟着大彬读源码 - Redis 4 - 服务器的事件驱动有什么含义?(上)
众所周知,Redis 服务器是一个事件驱动程序.那么事件驱动对于 Redis 而言有什么含义?源码中又是如何实现事件驱动的呢?今天,我们一起来认识下 Redis 服务器的事件驱动. 对于 Redis ...
- 跟着大彬读源码 - Redis 1 - 启动服务,程序都干了什么?
一直很羡慕那些能读 Redis 源码的童鞋,也一直想自己解读一遍,但迫于 C 大魔王的压力,解读日期遥遥无期. 相信很多小伙伴应该也都对或曾对源码感兴趣,但一来觉得自己不会 C 语言,二来也不知从何入 ...
- 跟着大彬读源码 - Redis 6 - 对象和数据类型(下)
继续撸我们的对象和数据类型. 上节我们一起认识了字符串和列表,接下来还有哈希.集合和有序集合. 1 哈希对象 哈希对象的可选编码分别是:ziplist 和 hashtable. 1.1 ziplist ...
- 跟着大彬读源码 - Redis 7 - 对象编码之简单动态字符串
Redis 没有直接使用 C 语言传统的字符串表示(以空字符串结尾的字符数组),而是构建了一种名为简单动态字符串(simple dynamic string)的抽象类型,并将 SDS 用作 Redis ...
- 跟着大彬读源码 - Redis 8 - 对象编码之字典
目录 1 字典的实现 2 插入算法 3 rehash 与 渐进式 rehash 总结 字典,是一种用于保存键值对的抽象数据结构.由于 C 语言没有内置字典这种数据结构,因此 Redis 构建了自己的字 ...
- 跟着大彬读源码 - Redis 9 - 对象编码之 三种list
目录 1 ziplist 2 skiplist 3 quicklist 总结 Redis 底层使用了 ziplist.skiplist 和 quicklist 三种 list 结构来实现相关对象.顾名 ...
- 跟着大彬读源码 - Redis 10 - 对象编码之整数集合
[TOC] 整数集合是 Redis 集合键的底层实现之一.当一个集合只包含整数值元素,并且元素数量不多时,Redis 就会使用整数集合作为集合键的底层实现. 1 整数集合的实现 整数集合是 Redis ...
- 跟着大彬读源码 - Redis 5 - 对象和数据类型(上)
相信很多人应该都知道 Redis 有五种数据类型:字符串.列表.哈希.集合和有序集合.但这五种数据类型是什么含义?Redis 的数据又是怎样存储的?今天我们一起来认识下 Redis 这五种数据结构的含 ...
随机推荐
- matlab 稀疏矩阵(sparse matrix)
参数的设置:spparms() spparms('spumoni', 3);:Set sparse monitor flag to obtain diagnostic output 1. 创建稀疏矩阵 ...
- postgresql 自带函数
替换函数 SELECT replace('abcdefabcdef', 'cd', 'XX') 得到 abXXefabXXef ------------------------------------ ...
- Bootstrap 分页翻页
@{ Layout = null;}<!DOCTYPE html><html><head> <meta name="viewport&q ...
- shell脚本配置ssh免密登陆
通过shell脚本配置免密登陆,分为两个脚本,一个是配置文件config.env,一个是正式脚本sshkey.sh. # config.envexport HOST_USER=(root) expor ...
- 用JavaScriptSerializer解析JSON
引用System.Web.Extensions using System.Web.Script.Serialization; var serializer = new JavaScriptSerial ...
- EasyUI之combogrid
html代码: <div class="item-label">@Html.L("Name"):</div> <input id= ...
- GzipStream的简单使用压缩和解压
压缩和解压都需要用到三个流实例,分别是文件读取流.文件写入流.压缩流. 读取流和写入流有多种形式,压缩流就一种GzipStream. 不同的是对于压缩,是需要用文件写入流作为创建压缩流实例的参数, 压 ...
- Delphi7 时钟(使用了多个自定义组件)
http://download.csdn.net/detail/akof1314/3073289
- .NET重思(三)-数组列表与数组的区别,栈集合和队列结合的区别
数组列表和数组十分相似,区别在于数组列表的容量是可以动态变化的,而数组的容量是固定的.数组即Array类,数组列表即ArrayList类,两者十分相似.不过,Array类在System命名空间下,Ar ...
- DateTimeToGreenUnix
@暗夜魔尊 { Unix date conversion support with time-zone detect } function DateTimeToGreenUnix(const AVal ...