Redis客户端
客户端定义
Redis服务器是典型的一对多服务器程序,通过使用由I/O多路复用技术实现文件事件处理器,Redis服务器使用单进程单线程的方式来处理命令请求,并与多个客户端进行网络通信。
对于每个与服务端进行连接的客户端,服务器都是为这些客户端建立对应的redis.h/redisClient结构来保存客户端当前的状态信息,以及相关功能需要用到的数据结构。
Redis服务器状态结构的clients属性是一个链表,保存了与服务器连接的客户端的状态结构。数据结构如下:
struct redisServer{
list *clients //一个保存所有客户端状态的链表
}
假设一个服务端与三个客户端对接,他的数据结构是这样的:
客户端属性
套接字描述符、名字、标志
数据结构标识:
typedef struct redisClient{
int fd;//套接字描述符
robj *name;//名字
int flags; //标识
}
套接字描述符
伪客户端的套接字描述符字段值为-1,伪客户端的处理命令来源于AOF文件或者Lua脚本,而不是网络。普通客户端的套接字标识符为大于-1的整数
名字
默认情况下,连接到服务端的客户端是没有名字的,但是可以通过设置name字段让客户端的身份更明确。
标志
客户端的标志属性记录了客户端的角色信息
flags属性可以是单个标志,也可以是多个标志的二进制或。
命令
命令参数与执行程序对应数据结构
typedef struct redisClient{
robj **argv //字符串数组,用来保存相应的命令内容
int argc //字符串数组的大小
struct redisCommand *cmd //命令所对应的redisCommand结构
}
服务器将客户端发送的命令请求存储到客户端状态的query_buf属性之后,服务器会对命令的内容进行分析,将分析的结果保存到 argv和argc(长度)属性上,具体的如果是像set key value这样的命令他的存储如下:
服务器进行命令的解析时,会从argv[0]获取值并与命令数据字典dict(命令表)进行匹配,找到对应的命令信息,存储到cmd属性。这个属性包含给定的参数个数、命令的总执行次数,命令的总耗时等统计信息。服务端通过调用cmd这个参数进行命令的执行。
缓冲区
输入缓冲区与输出缓冲区的结构表示:
typedef struct redisClient{
sds querybuf; //输入缓冲区
//固定大小输出缓冲区
char buf[REDIS_REPLY_CHUNK_BYTES]
int bufpos
list *reply; //可变大小输出缓冲区
}
输入缓冲区是保存客户端输入的命令信息,所以用sds的结构进行存储,它的大小会根据内容动态的缩小或者扩大,但是最大大小如果超过1GB,服务端就会关闭这个客户端。
每个客户端都会有两个输出缓冲区,一个缓冲区大小是固定的,一个缓冲区大小是可变的。
固定大小缓冲区,通过buf数组和bufpos,两个属性组合,buf数组的大小是固定的,bufpos则记录了buf数组目前已经使用的字节数量。固定大小缓冲区主要用来保存长度较小的回复。
当固定大小缓冲区空间使用殆尽或者回复消息过大,则会开始启用可变长大小的输出缓冲区,它的数据结构是通过链表来连接一个或者多个的字符串对象,具体结构如下:
身份验证
身份验证的数据结构是:
typedef struct redisClient{
int authenticated;//身份验证
}
如果该字段的值为0表示客户端未通过身份验证,如果该值为1表示客户端通过身份验证。
时间
ctime: 记录了创建客户端的时间,这个时间可以用来计算客户端和服务端已经连接了多少秒
lastinteraction:记录客户端和服务端最后一次进行互动的时间,可以用来计算客户端空转时长
obuf_soft_limit_reached_time:记录输出缓冲区第一次到达软性限制的时间。
客户端创建与关闭
普通客户端
通过网络与服务器连接的普通客户端,在客户端使用connect函数连接到服务器的时候,服务器就会调用连接事件处理器为客户端创建相应的客户端状态,将它添加到clients链表的末尾。
客户端被服务端关闭有很多种情况。
服务器会采用两种模式来限制客户端缓冲区大小:
-
硬性限制:如果缓冲区大小超出硬性限制的大小,服务端会主动关闭客户端
-
软性限制:如果客户端没有超出硬性限制,但是在配置的软性限制时长中,持续超出软性限制大小,则服务端会自动关闭客户端,否则不关闭
Lua脚本的伪客户端
服务器会在初始化时,创建负责执行Lua脚本的中包含Redis命令的伪客户端,将这个伪客户端关联到服务器状态结构中的lua_client属性中。
struct redisServer{
redisClient *lua_client;
}
lua_client伪客户端在服务器运行的整个生命周期都会一直存在,只有服务器被关闭的时候,这个客户端才会关闭。
AOF的伪客户端
服务器在载入AOF文件的时候,会创建用于执行AOF文件的Redis命令伪客户端,并在载入完成之后,关闭这个伪客户端。
总结
- 服务器状态结构使用 clients 链表连接起多个客户端状态, 新添加的客户端状态会被放到链表的末尾。
- 客户端状态的 flags 属性使用不同标志来表示客户端的角色, 以及客户端当前所处的状态。
- 输入缓冲区记录了客户端发送的命令请求, 这个缓冲区的大小不能超过 1 GB 。
- 命令的参数和参数个数会被记录在客户端状态的 argv 和 argc 属性里面, 而 cmd 属性则记录了客户端要执行命令的实现函数。
- 客户端有固定大小缓冲区和可变大小缓冲区两种缓冲区可用, 其中固定大小缓冲区的最大大小为 16 KB , 而可变大小缓冲区的最大大小不能超过服务器设置的硬性限制值。
- 输出缓冲区限制值有两种, 如果输出缓冲区的大小超过了服务器设置的硬性限制, 那么客户端会被立即关闭; 除此之外, 如果客户端在一定时间内, 一直超过服务器设置的软性限制, 那么客户端也会被关闭。
- 当一个客户端通过网络连接连上服务器时, 服务器会为这个客户端创建相应的客户端状态。 网络连接关闭、 发送了不合协议格式的命令请求、 成为 CLIENT_KILL 命令的目标、 空转时间超时、 输出缓冲区的大小超出限制, 以上这些原因都会造成客户端被关闭。
- 处理 Lua 脚本的伪客户端在服务器初始化时创建, 这个客户端会一直存在, 直到服务器关闭。
- 载入 AOF 文件时使用的伪客户端在载入工作开始时动态创建, 载入工作完毕之后关闭。