客户端
输入缓冲区。
客户端状态的输入缓冲区用于保存客户端发送的命令请求:
typedef struct redisClient {
// ...
sds querybuf;
// ...
}redisClient;
例子
举个例子,如果客户端向服务器发送了以下命令请求:
SET key value
那么客户端状态的qureybuf属性将是一个包含以下内容的SDS值
*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n
如图所示占了这个SDS值以及querybuf属性的样子。输入缓冲区的大小会
根据输入内容动态地缩小或者扩大,但它的最大大小不能超过1GB,否则服务器将关闭这个客户端
命令与命令参数
在服务器将客户端发送的命令请求保存到客户端状态的querybuf属性之后,服务器将对命令请求的内容进行分析,并将得出的命令参数以及命令参数的个数分别保存到客户端状态的argv属性和argc属性:
typedef struct redisClient {
// ...
robj **argv;
int argc;
// ...
}redisClient
argv属性是一个数组,数组中的每个项都是一个字符串对象,其中argv[0]是要执行的命令,而之后的其他项则是传给命令的参数。
argc属性则是负责记录argv数组的长度。
例子
- 举个例子,图中展示的客户端状态中,argc属性的值伪3,而不是2,
因为命令的名字"SET"本身也是一个参数
命令的实现函数
当服务器从协议内容中分析并得出argv属性和argc属性的值之后,服务器将根据项argv[0]的值,在命令表中查找命令所对应的命令实现函数。
图中展示了一个命令表示例,该表是一个字典,字典的键是一个SDS结构,保存了命令的名字,字典的值是命令所对应的rediCommand结构,这个结构保存了命令的实现函数、命令的标志、命令应该给定的参数个数、命令的总执行次数和总消耗时长等统计信息。当程序在命令表中成功找到argv[0]所对应的redisCommand结构时,它会将客户端状态的cmd
指针指向这个结构:
typedef struct redisClient {
// ...
struct redisCommand *cmd;
// ...
}redisClient
之后,服务器就可以使用cmd属性所指向的redisCommand结构,以及argv、argc属性中保存的命令参数信息,调用命令实现函数,执行客户端指定的命令
图中演示了服务器在argv[0]为"SET"时,查找命令表并将客户端状态的
cmd指针指向目标redisCommand结构的整个过程。针对命令表的查找操作不区分输入字母的大小写,所以无论argv[0]是"SET"、“set” 、或者"Set"等等,查找的结构都是相同的。
输出缓冲区
z执行命令所得的命令回复会被保存在客户端状态的输出缓冲区里面,每个客户端都有两个输出缓冲区可用,一个缓冲区的大小是固定的,另一个缓冲区的大小是可变的:
- 1.固定大小的缓冲区用于保存那些长度比较小的回复,比如OK、间段的字符串值、整数值、错误回复等等
- 2.可变大小的缓冲区用于保存那些长度比较大的回复,比如一个非常长的字符串值,一个由很多项组成的列表,一个包含了很多元素的集合等等。客户端的固定大小缓冲区由buf和bufpos两个属性组成:
typedef struct redisClient {
// ...
char buf[REDIS_REPLY_CHUNK_BYTS];
int bufpos;
// ...
}redisClient;
buf是一个大小为REDIS_REPLY_CHUNK_BYTES字节的字节数组,而bufpos属性则记录了buf数组目前已使用的字节数量。REDIS_REPLY_CHUNK_BYTES常量目前的默认值为16*1024,也就是说,buf数组的默认大小为16KB.如图展示了一个使用固定大小缓冲区来保存返回值+OK\r\n的例子。当buf数组的空间已经用完,或者回复因为太大而没办法放进buf数组里面时,服务器就会开始使用可变
大小缓冲区。可变大小缓冲区由reply链表和一个或多个字符串对象组成:
typedef struct redisClient {
// ...
list *reply;
// ...
}redisClient
通过使用链表l来连接多个字符串对象,服务器可以为客户端保存一个非常长的命令回复,而不必受到固定
大小缓冲区16KB大小的限制。
例子
如图展示了一个包含三个字符串对象的reply链表