redis-客户端
1、客户端属性
redis> CLIENT list
addr=127.0.0.1:53428 fd=6 name= age=1242 idle=0 ...
addr=127.0.0.1:53469 fd=7 name= age=4 idle=4 ...
typedef struct redisClient {
// ...
int fd;
robj *name;
int flags;
sds querybuf;
robj **argv;
int argc;
struct redisCommand *cmd;
char buf[REDIS_REPLY_CHUNK_BYTES];
int bufpos;
list *reply;
int authenticated;
time_t ctime;
time_t lastinteraction;
time_t obuf_soft_limit_reached_time;
// ...
} redisClient;
-
套接字描述符fd
- 伪客户端(fake client)的
fd
属性的值为-1
: 伪客户端处理的命令请求来源于 AOF 文件或者 Lua 脚本, 而不是网络, 所以这种客户端不需要套接字连接, 自然也不需要记录套接字描述符。 目前 Redis 服务器会在两个地方用到伪客户端, 一个用于载入 AOF 文件并还原数据库状态, 而另一个则用于执行 Lua 脚本中包含的 Redis 命令。 - 普通客户端的
fd
属性的值为大于-1
的整数: 普通客户端使用套接字来与服务器进行通讯, 所以服务器会用fd
属性来记录客户端套接字的描述符。 因为合法的套接字描述符不能是-1
, 所以普通客户端的套接字描述符的值必然是大于-1
的整数。
- 伪客户端(fake client)的
-
名字name
- 使用 CLIENT_SETNAME 命令可以为客户端设置一个名字, 让客户端的身份变得更清晰
-
标志flags
flags
属性的值可以是单个标志:flags = <flag>
- 多个标志的二进制或:
flags = <flag1> | <flag2> | ...
-
输入缓冲区querybuf
- 客户端状态的输入缓冲区用于保存客户端发送的命令请求:
- 如果客户端向服务器发送了以下命令请求:
SET key value
- 客户端状态的
querybuf
属性将是一个包含以下内容的 SDS 值:*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n
- 如果客户端向服务器发送了以下命令请求:
- 客户端状态的输入缓冲区用于保存客户端发送的命令请求:
-
命令与命令参数(argv argc)
argv
属性是一个数组, 数组中的每个项都是一个字符串对象: 其中argv[0]
是要执行的命令, 而之后的其他项则是传给命令的参数。argc
属性则负责记录argv
数组的长度。
-
命令的实现函数cmd
-
该表是一个字典, 字典的键是一个 SDS 结构, 保存了命令的名字, 字典的值是命令所对应的
redisCommand
结构, 这个结构保存了命令的实现函数、 命令的标志、 命令应该给定的参数个数、 命令的总执行次数和总消耗时长等统计信息。 -
当程序在命令表中成功找到
argv[0]
所对应的redisCommand
结构时, 它会将客户端状态的cmd
指针指向这个结构:
-
-
输出缓冲区(buf bufpos)
- 执行命令所得的命令回复会被保存在客户端状态的输出缓冲区里面, 每个客户端都有两个输出缓冲区可用, 一个缓冲区的大小是固定的, 另一个缓冲区的大小是可变的:
- 固定大小的缓冲区用于保存那些长度比较小的回复, 比如
OK
、简短的字符串值、整数值、错误回复,等等。 - 可变大小的缓冲区用于保存那些长度比较大的回复, 比如一个非常长的字符串值, 一个由很多项组成的列表, 一个包含了很多元素的集合, 等等。
- 固定大小的缓冲区用于保存那些长度比较小的回复, 比如
- 客户端的固定大小缓冲区由
buf
和bufpos
两个属性组成:buf
是一个大小为REDIS_REPLY_CHUNK_BYTES
字节的字节数组, 而bufpos
属性则记录了buf
数组目前已使用的字节数量。REDIS_REPLY_CHUNK_BYTES
常量目前的默认值为16*1024
, 也即是说,buf
数组的默认大小为 16 KB
- 可变大小缓冲区由
reply
链表和一个或多个字符串对象组成:- 通过使用链表来连接多个字符串对象, 服务器可以为客户端保存一个非常长的命令回复, 而不必受到固定大小缓冲区 16 KB 大小的限制
- 执行命令所得的命令回复会被保存在客户端状态的输出缓冲区里面, 每个客户端都有两个输出缓冲区可用, 一个缓冲区的大小是固定的, 另一个缓冲区的大小是可变的:
-
身份验证
- 客户端状态的
authenticated
属性用于记录客户端是否通过了身份验证:- 如果
authenticated
的值为0
, 那么表示客户端未通过身份验证; 如果authenticated
的值为1
, 那么表示客户端已经通过了身份验证
- 如果
- 客户端状态的
-
时间
ctime
属性记录了创建客户端的时间, 这个时间可以用来计算客户端与服务器已经连接了多少秒lastinteraction
属性记录了客户端与服务器最后一次进行互动(interaction)的时间, 这里的互动可以是客户端向服务器发送命令请求, 也可以是服务器向客户端发送命令回复。obuf_soft_limit_reached_time
属性记录了输出缓冲区第一次到达软性限制(soft limit)的时间
2、客户端的创建与关闭
-
创建普通客户端
- 如果客户端是通过网络连接与服务器进行连接的普通客户端,那么在客户端使用 connect 函数连接到服务器时,服务器就会调用连接事件处理器,为客户端创建相应的客户端状态,并将这个新的客户端状态添加到服务器状态结构 clients链表的末尾。
-
关闭普通客户端
- 如果客户端进程退出或者被杀死,那么客户端与服务器之间的网络连接将被关闭
- 如果客户端向服务器发送了带有不符合协议格式的命令请求
- 如果客户端成为了 CLIENT KILL 命令的目标
- 如果用户为服务器设置了timeout配置选项,那么当客户端的空转时间超过timeout 选项设置的值时,客户端将被关闭。不过 timeout 选项有一些例外况:如果客户端是主服务器(打开了REDIS_MASTER标志),从服务器(打开了 REDISSLAVE标志),正在被BLPOP等命令阻塞(打开了 REDIS_BLOCKED志),或者正在执行 SUBSCRIBE、PSUBSCRIBE 等订阅命令,那么即使客户端的空转时间超过了 timeout 选项的值,客户端也不会被服务器关闭。
- 如果客户端发送的命令请求的大小超过了输人缓冲区的限制大小(默认为1GB)
- 如果要发送给客户端的命令回复的大小超过了输出缓冲区的限制大小
- 方面介绍输出缓冲区的时候提到过,可变大小缓冲区由一个链表和任意多个字符串对象
-
伪客户端
- 处理 Lua 脚本的伪客户端在服务器初始化时创建, 这个客户端会一直存在, 直到服务器关闭。
- 载入 AOF 文件时使用的伪客户端在载入工作开始时动态创建, 载入工作完毕之后关闭。