目录前言1. 事件1.1 文件事件1.1.1 I/O 多路复用程序的实现1.1.2 事件类型与 api1.1.3 文件事件的处理器1.2 时间事件1.2.1 API1.2.2 serverCron 函数1.3 事件的调度与执行2. 客户端
参考资料:《Redis设计与实现 第二版》;
第二部分为单机数据库的实现,主要由以下模块组成:数据库、持久化、事件、客户端与服务器;
本篇将介绍 Redis 中的事件与客户端,其中事件有两种:文件事件与时间事件;客户端主要介绍属性,以及创建与关闭;
ae.c/aeCreateFileEvent
:将给定套接字的给定事件加入到 I/O 多路复用程序的监听范围之内,并对事件和事件处理器进行关联;ae.c/aeGetFileEvents
:接受一个套接字描述符,返回该套接字正在被监听的事件类型;ae.c/aeWait
:在给定时间内阻塞并等待套接字的给定类型事件产生;ae.c/aeApiPoll
:在指定时间内,阻塞并等待所有被 aeCreateFileEvent 函数设置为监听状态的套接字产生文件事件;ae.c/aeProcessEvents
:文件事件分派器;ae.c/aeGetApiName
:返回当前 I/O 多路复用程序底层的多路复用函数库名称;连接应答处理器:networking.c/accepttcpHandler
;
命令请求处理器:networking.c/readQueryFromClient
;
networking.c/sendReplyToClient
;ae.h/AE_NOMORE
;ae.h/AE_NOMORE
的整数值ae.c/aeCreateTimeEvent
:创建时间事件,这个时间事件将在当前时间的 milliseconds 毫秒后到达,事件处理器为 proc;ae.c/aeDeleteFileEvent
:根据 id 从服务器中删除对应的时间事件;ae.c/aeSearchNearestTimer
:返回到达时间距离当前时间最接近的那个时间事件;ae.c/processTimeEvents
:时间事件执行器,遍历所有时间事件,并调用处理器处理已到达的时间事件。实际不存在,处理时间事件实际由 ae.c/aeProcessEvents
函数负责;redis.c/serverCron
函数执行,其主要工作包括:
ae.c/aeProcessEvents
函数负责;ae.c/aeProcessEvents
函数的处理逻辑:
aeApiPoll
函数的最大阻塞时间由到达时间最接近当前时间的事件事件决定;Redis 服务器状态结构 clients 属性是一个链表,保存了所有客户端状态:
struct redisServer{
//...
//客户端链表
list *clients;
};
typedef struct redisClient{
//...
//嵌套字描述符
int fd;
} redisClient;
fd
属性记录客户端套接字的描述符;typedef struct redisCilent{
//...
//名字
robj *name;
} redisClient;
客户端的标志属性 flags 记录了客户端的角色,以及客户端目前所处的状态:
typedef struct redisClient{
//...
//标志
int flags;
} redisClient;
每个标志使用一个常量表示,一部分记录了客户端角色。另一部分标志记录了客户端目前所处状态;
通常情况下,Redis 只会将那些对数据库进行修改的命令写入 AOF 文件,并复制到各个从服务器;
PUBSUB 和 SCRIPT LOAD 命令除外,带有副作用,前者会改变接受消息的客户端状态,服务端需要使用 REDIS_FORCE_AOF 标志。后者修改服务器状态,服务器状态需要REDIS_FORCE_AOF 和 REDIS_FORCE_REPL 标志;
标志在 redis.h
文件里面定义;
客户端状态的输入缓冲区用于保存客户端发送的命令请求;
typedef struct redisClient{
//...
//输入缓冲区
sds querybuf;
} redisClient;
服务器将客户端的命令请求保存到客户端状态的 querybuf
属性后,服务器对命令请求内容分析,得出命令参数及参数个数,保存到客户端状态的 argv
和 arGC
属性;
argv
属性是一个数组,数组中的每个项都是一个字符串对象,argv[0]
是要执行的命令,之后的其他项是传给命令的参数;
argc
属性负责记录 argv
数组的长度;
typedef struct redisClient{
//...
//命令及其参数数组
robj **argv;
//数组的长度
int argc;
} redisClient;
argv[0]
的值,在命令表中查找命令所对应的命令实现函数;在命令表找到 argv[0]
对应的 redisCommand
结构后,会将客户端状态的 cmd
指针指向这个结构;
typedef struct redisClient{
//...
//命令的实现函数
struct redisCommand *cmd;
} redisClient;
固定大小的缓冲区:保存长度较小的回复,默认 16KB;
typedef struct redisClient{
//...
//字节数组,保存缓冲区
char buf[REDIS_REPLY_CHUNK_BYTES];
//buf已使用的字节数量
int bufpos;
}
可变大小的缓冲区:保存长度较大的回复,使用链表;
typedef struct redisClient{
//...
//链表
list *reply;
} redisClient;
客户端状态的 authenticated
属性用于记录客户端是否通过了身份验证;
typedef struct redisClient{
//...
//身份验证属性
int authenticated;
} redisClient;
authenticated
属性仅在服务器启用身份验证功能时用。若该功能未开启,即使 authenticated
值为 0,服务器也不会拒绝客户端的命令请求;
typedef struct redisClient{
//...
//记录了创建客户端的时间
time_t ctime;
//记录客户端与服务器最后一次进行互动(interaction)的时间,即空转时间
time_t lastinteraction;
//输出缓冲区第一次到达软性限制(soft limit)的时间
time_t obuf_soft_limit_reached_time;
} redisClient;
一个普通客户端可以因为多种原因而被关闭;
服务器使用两种模式限制客户端输出缓冲区的大小:
服务器在初始化时创建负责执行 Lua 脚本中包含的 Redis 命令的伪客户端,并将其关联在服务器状态结构的 lua_client
属性;
struct redisServer{
//...
//Lua 脚本的伪客户端
redisClient *lua_client;
};
伪客户端在服务器运行的整个生命周期中会一直存在,只有服务器被关闭时,这个客户端才会被关闭;
--结束END--
本文标题: Redis | 第6章 事件与客户端《Redis设计与实现》
本文链接: https://www.lsjlt.com/news/8966.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-05-14
2024-05-14
2024-05-14
2024-05-14
2024-05-14
2024-05-13
2024-05-13
2024-05-13
2024-05-13
2024-05-12
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0