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

上次咱们经过问题“启动服务器,程序都干了什么?”,跟着源码,深刻了解了 Redis 服务器的启动过程。算法

既然启动了 Redis 服务器,那咱们就要连上 Redis 服务干些事情。这里咱们能够经过 redis-cli 测试。数据库

如今客户端和服务器都准备好了,那么Redis 客户端和服务器如何创建链接?服务器又是如何响应客户端的请求呢?bash

1 链接服务器

客户端和服务器进行通信,首先应该就是创建链接。接下来,咱们来看下 redis-cli 与服务器的链接过程。服务器

还记得咱们上次使用 gdb 调试程序的步骤吗?让咱们对 redis-cli 再来一次,看看源码的执行步骤。在开始以前,记得在编辑器打开 redis-cli.c,定位到 main 函数的位置,毕竟 gdb 看代码没有编辑器看着舒服。并发

debug 步骤以下:socket

# 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

执行完上述步骤,咱们会进入以下界面:编辑器

图 1 - 进入 redis-cli main 函数

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

1.1 初始化客户端配置

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

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

图 2 - 启动 redis-cli 携带的 -p 参数被赋值给 hostport 配置项

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 所示:

图 3 - 客户端接收并发送命令请求的过程

所以,对于咱们上面的命令请求,客户端会转成:

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

而后发给服务器。

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

相关文章
相关标签/搜索