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