跟着大彬读源码 - Redis 2 - 服务器如何响应客户端请求?(上)

码海绘月使
• 阅读 1942

上次我们通过问题“启动服务器,程序都干了什么?”,跟着源码,深入了解了 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

执行完上述步骤,我们会进入如下界面:

跟着大彬读源码 - Redis 2 - 服务器如何响应客户端请求?(上)

这时候我们就可以回到编辑器页,看看对 main 函数中哪一行比较感兴趣,就停下来研究研究。到了 2618 行,我们会看到有执行 parseOptions 这个函数,看名字,好像是初始化一些可选项。那就进去看看呗。

1.1 初始化客户端配置

函数执行步骤:main -> parseOptions -> main

我们会看到,在执行 redis-cli 时携带的参数都是在这个函数中解析,比如我们启动的时候带着的 -p 参数,会在 996 行被解析到,同时赋值给客户端的 hostport 配置项。如下图:

跟着大彬读源码 - Redis 2 - 服务器如何响应客户端请求?(上)

1.2 客户端启动模式

函数执行步骤:main

回到 main 函数,会看到后面的代码会出现很多 cliConnect 函数。要注意的是,这里并不表示 redis-cli 会执行多次 cliConnect 函数。实际上,每一个 if 语句块,都代表着客户端的一种连接模式,3.2.13 版本支持以下模式:

  1. Latency mode:延迟模式。redis-cli --latency -p 8379 用来测试客户端与服务器连接的延迟。还有 --history--dist 可选项,用来展示不同的形式。
  2. Slave mode:模拟从节点模式。
  3. Get RDB mode:生成并发送 RDB 持久化文件,保存在本地。
  4. Pipe mode:管道模式。将命令封装成指定数据格式,批量发送给 redis 服务器执行。
  5. Find big keys:统计 bigkey 的分布。
  6. Stat mode:统计模式。实时展示服务器的统计信息。
  7. Scan mode:扫描指定模式的键,相当于 scan 模式。
  8. 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 函数中会看到 getaddrinfoconnect 函数的调用,这里就是建立 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 所示:

跟着大彬读源码 - Redis 2 - 服务器如何响应客户端请求?(上)

因此,对于我们上面的命令请求,客户端会转成:

"*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n"

然后发给服务器。

以上是客户端发送命令给服务器的过程,在下一节中,我们再来认识服务器是如何响应客户端请的。

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
4年前
ubuntu 18.04 安装 Redis
Redis安装Redis是一款内存键值存储,以其灵活性,性能和广泛的语言支持而闻名。本教程将演示如何在Ubuntu18.04服务器上安装和配置Redis。主要内容包括:1.安装Redis2.Redis配置3.Redis控制一、安装Redis使用apt从官方Ubuntu
Stella981 Stella981
4年前
Redis命令与 键对
Redis命令Redis命令用于在redis服务上执行操作。要在redis服务上执行命令需要一个redis客户端语法Redis客户端的基本语法为:$rediscli中文出现乱码问题要在redis
Stella981 Stella981
4年前
Redis应用学习(三)——Jedis客户端的简单使用介绍
1\.Redis的Java客户端程序——Jedis  1.在Redis的安装目录中rediscli是一个Redis的Shell语言写的客户端,通过运行该客户端就可以实现一系列Redis操作,而如果要使用Java语言的Redis客户端,就必须要使用Jedis相关的包。首先导入Jedis相关的包
Stella981 Stella981
4年前
Redis存储方式RDB和AOF介绍及持久化的重要性
更多精彩和干货和你分享前言redis可以干很多事情,我们这里的背景是将redis作为缓存服务器还讨论的。如果部署了一个redis,主从也好,cluster也罢,我将一些数据存储在它上面,如果没有持久化的话,redis只会存储到内存中,那如果遇到灾难性故障,就会丢失所有数据。这对企业级的应用来说,是个噩梦。如果我们能将数据持久化
Wesley13 Wesley13
4年前
ubuntu 下搭建redis和php的redis的拓展
系统环境:腾讯云服务器,ubuntu16.0.4.4,php7.0一.安装redis服务sudoaptgetinstallredisserver安装好的redis目录在/etc/redis\也可使用命令whereisredis查找\启动:sudoserviceredisserverst
Stella981 Stella981
4年前
Redis分布式缓存系统Lua脚本食用指引
Redis为什么添加Lua支持redislua脚本出现之前Redis是没有服务器端运算能力的,主要是用来存储,用做缓存,运算是在客户端进行,这里有两个缺点:一、如此会破坏数据的一致性,试想如果两个客户端先后获取(get)一个值,它们分别对键值做不同的修改,然后先后提交结果,最终Redis服务器中的结果肯定不是某一方客户端所预期的
Stella981 Stella981
4年前
Redis使用pipeLine批量获取数据加快接口响应速度
一、背景需求:redis通过tcp来对外提供服务,client通过socket连接发起请求,每个请求在命令发出后会阻塞等待redis服务器进行处理,处理完毕后将结果返回给client。其实和一个http的服务器类似,一问一答,请求一次给一次响应。而这个过程在排除掉redis服务本身做复杂操作时的耗时的话,可
Stella981 Stella981
4年前
Redis应用学习——缓存的使用与设计
1\.缓存的收益与成本  1.收益:通过缓存加速读写速度。在内存中读写比硬盘速度快降低数据库服务器的负载。比如业务端的请求的数据大多数都由Redis服务器来处理,大大减轻MySQL服务器的压力  2.成本:数据不一致问题,比如Redis服务器与数据库服务器之间的某些数据可能会发