上次咱们经过问题“启动服务器,程序都干了什么?”,跟着源码,深刻了解了 Redis 服务器的启动过程。算法
既然启动了 Redis 服务器,那咱们就要连上 Redis 服务干些事情。这里咱们能够经过 redis-cli 测试。数据库
如今客户端和服务器都准备好了,那么Redis 客户端和服务器如何创建链接?服务器又是如何响应客户端的请求呢?bash
客户端和服务器进行通信,首先应该就是创建链接。接下来,咱们来看下 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
执行完上述步骤,咱们会进入以下界面:编辑器
这时候咱们就能够回到编辑器页,看看对 main
函数中哪一行比较感兴趣,就停下来研究研究。到了 2618 行,咱们会看到有执行 parseOptions
这个函数,看名字,好像是初始化一些可选项。那就进去看看呗。函数
函数执行步骤:main
-> parseOptions
-> main
。测试
咱们会看到,在执行 redis-cli
时携带的参数都是在这个函数中解析,好比咱们启动的时候带着的 -p
参数,会在 996 行被解析到,同时赋值给客户端的 hostport 配置项。以下图:
函数执行步骤:main
。
回到 main
函数,会看到后面的代码会出现不少 cliConnect
函数。要注意的是,这里并不表示 redis-cli 会执行屡次 cliConnect
函数。实际上,每个 if
语句块,都表明着客户端的一种链接模式,3.2.13 版本支持如下模式:
redis-cli --latency -p 8379
用来测试客户端与服务器链接的延迟。还有 --history
和 --dist
可选项,用来展现不一样的形式。函数执行步骤: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 链接的地方。
函数执行步骤: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
命令为例,来看看命令的执行过程。
当用户在客户端键入一个命令请求时,客户端会将这个命令请求按协议格式转换,而后经过链接到服务器的套接字,将转换后的命令请求发送给服务器,如图 3 所示:
所以,对于咱们上面的命令请求,客户端会转成:
"*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n"
而后发给服务器。
以上是客户端发送命令给服务器的过程,在下一节中,咱们再来认识服务器是如何响应客户端请的。