《Redis设计与实现》之第十三章:Redis客户端
Redis服务器是典型的一对多服务器程序:一个服务器可以和多个客户端建立网络连接,Redis使用单线程单进程的方式来处理命令请求(通过IO多路转接实现),并和多个服务端进行网络通信。
每个和服务端进行连接的客户端,服务端都为这些客户端建立了对应的redisClient结构(保存客户端状态),这个结构保存客户端当前的状态信息。所有的redisClient保存在clients属性中,这个clients是一个链表,如果想查找某个指定的客户端,可以通过遍历clients链表来完成。
一,客户端属性
分两类:
比较通用的属性:无论客户端执行的是什么,都要用到这些属性
特定功能相关的属性:比如:操作数据库时用到的db属性,执行事物时用到的mstate属性,
1.套接字描述符: int fd
客户端状态的fd属性记录客户端正在使用的套接字描述符
typedef struct redisClient{
int fd;
}
伪客户端的fd属性值为-1,比如AOF文件或Lua脚本,这种客户端不需要套接字连接
普通客户端的fd属性的值为大于-1的整数:普通客户端使用套接字和服务器进行通信,所以服务器会用fd属性记录客户端套接字的描述符。
2.名字
默认情况下,一个连接到服务器的客户端是没有名字的。
typedef struct redisClient{
robj *name;
}
如果客户端没有为自己设置名字,那么客户端状态的name属性指向NULL指针,如果客户端为自己设置了名字,那么name属性指向一个字符串对象,该对象保存着客户端的名字
3.标志
客户端标志属性flags记录客户端的角色(role),以及客户端目前所处的状态:
typedef struct redisClient{
int flags;
}
比如:REDIS_MASTER, REDIS_SLABE, REDIS_BLOCKED(表示客户端正在被brpop,blpop等命令阻塞)
4.输入缓冲区
用来保存客户端发送的命令请求:
typedef struct redisClient{
sds querybuf;
}
缓冲区大小根据输入内容动态扩大或缩小,最大不能超过1GB
5.命令与命令参数
服务器把客户端发送的命令请求保存到客户端的querybuf属性中后,服务器将对命令请求的内容进行分析,并将得到的命令参数和命令参数的个数分别保存到客户端状态的argv属性和argc属性中:
typedef struct redisClient{
robj **argv; //数组,数组中的每一项都是一个字符串对象
int argc;
}
其中argv[0]是要执行的命令,之后的其他项是传给命令的参数
6.命令的实现函数
服务器根据argv[0]的值,在命令表中查找命令所对应的命令实现函数(比如:argv[0]存的是set命令)。命令和命令实现函数保存在dict结构中,键保存了命令的名字,值保存命令所对应的redisCommend结构,这个结构保存了命令的实现函数。
7.输出缓冲区
命令回复保存在客户端状态的输出缓冲区中。
缓冲区有两种:
固定大小的字节数组
大小可变的链表
8.身份验证
客户端状态的authenticated属性用于记录客户端是否通过身份验证
typedef struct redisClient{
int authenticated;
}
如果authenticated=0,表示客户端没有通过身份验证。如果authenticated=1,表示客户端通过了身份验证。
注意:authenticated属性只有在服务器启用了身份验证功能时使用。
9.时间
typedef struct redisClient{
time_t ctime;//创建客户端的时间
time_t lastinteraction;//客户端与服务器最后一次进行互动的时间,
time_t obuf_soft_limit_reached_time;//输出缓冲区第一次到达软性限制的时间
}
二,客户端的创建和关闭
1.创建普通客户端
客户端使用connect函数连接到服务器,服务器会调用连接事件处理器,为客户端创建相应的客户端状态,并把这个新的客户端状态添加到服务器状态结构clients链表的尾部。
2.关闭普通客户端
为了避免客户端的回复过大,占用过多的服务器资源,服务器会时刻检查客户端的输出缓冲区大小,并在缓冲区的大小超过范围时,执行相应的限制操作。有两种模式来限制客户端输出缓冲区的大小:
硬性限制:如果输出缓冲区的大小超过硬性限制所设置的大小,那么服务器立刻关闭客户端
软性限制:客户端在一定时间内,一直超过超过服务器设置的软性限制,客户端也会被关闭