Redis 使用单线程+I/O多路复用技术实现文件事件处理器,并与多个客户端进行网络通信。对于每个和服务器连接的客户端,服务器都为它创建了相应的客户端状态(redisClient结构)。
typedef redisServer {
list *clients; // 一个链表,保存了所有客户端状态
// ...
}
用链表存储客户端状态
客户端属性
这里只介绍一个通用的属性,另一类是特定功能的属性如执行 WATCH 命令用到的 watched_keys 等等。
typedef struct redisClient {
// ...
int fd; // 套接字文件描述符(伪客户端用 -1,其他为大于 -1 的整数)
robj *name; // 客户端可用 CLIENT SETNAME 命令设置
int flags; // 用于记录客户端的角色和状态,如 REDIS_MASTER|REDIS_PRE_PSYNC 等等
sds querybuf; // 输入缓冲区,请求过来后先存放存在这里,<= 1G
robj **argv; // 一个数组存放命令参数列表
int argc; // 参数个数(argv的长度)
struct redisCommand *cmd; // 指向实现请求命令(argv[0])的函数
char buf[REDIS_REPLY_CHUNK_BYTES]; // 输出缓冲区1(固定大小 16K)
int bufpos; // 固定大小输出缓冲区 1 中有效内容的长度
list *reply; // 输出缓冲区2(可变大小),用链表实现
int authenticated; // 是否通过了身份验证(0/1),服务器关闭验证时无效
time_t ctime; // 创建客户端的时间 created time
time_t lastinteraction; // 最后互动的时间
time_t obuf_soft_limit_reached_time; // 输出缓冲区第一次到达软性限制的时间
}
客户端的种类
客户端细分有三种:
- 普通客户端:使用网络进行通信的客户端
- Lua 脚本的伪客户端:lua 客户端与服务器的生命周期一致
- AOF 文件的伪客户端:仅在载入 AOF 时存在
关闭客户端的场景
- 客户端进程退出
- 客户端发送了不符合协议的命令请求(被服务器关闭)
- 客户端成为了 CLIENT KILL 的目标
- 服务器配置了 timeout 属性,客户端空转时间超时
- 客户端请求超过输入缓冲区 1G 大小
- 返回客户端的输出超过了输出缓冲区的限制大小
其中最后一条,输出缓冲区虽然用了链表来存,但对其限制还有一定的规则。两种规则:
- 硬性限制:超出则立即关闭客户端;
- 软性限制:持续超出软限一定时间后关闭客户端。
网友评论