4 事件
Redis服务器是一个事件驱动程序,需要处理两类事件,分别是文件事件和时间事件:
4.1 文件事件
文件事件处理器:基于Reactor模式的网络事件处理器
4.1.1 文件事件处理器的构成
socket、I/O多路复用程序、文件事件分派器、事件处理器:
IO多路复用程序通过队列向文件事件分派器传送套接字,当上一个socket处理完毕之后,才能传送下一个socket;每一个处理器都是一个函数:
4.1.2 IO多路复用程序的实现
通过包装select、epoll、evport、kqueue这些常见的IO多路复用函数库来实现的,每个函数库对应一个源文件
IO多路复用程序可以选择不同的函数库来作为底层实现,因为Redis为每个库实现了相同的API:
性能:evport>epoll>kqueue>select,#include宏中定义了规则:程序编译时会自动选择性能最高的库作为IO多路复用程序的底层实现
4.1.3 事件类型
ae.h/AE_READABLE、ae.h/AEWRITABLE:
同时出现时,优先处理AE_READABLE,即服务器先读后写
4.1.4 API
4.1.5 常见文件事件处理器
连接应答处理器、命令请求处理器、命令回复处理器、复制处理器(不常用)作用:
- 连接应答处理器
networking.c/acceptTcpHandler函数为连接应答处理器,用于对连接服务器监听socket的客户端进行应答;具体实现为sys/socket.h/accept函数的包装
流程:
- 命令请求处理器
networking.c/readQueryFromClient函数为命令请求处理器,用于从socket中读入客户端发送的命令请求;具体实现为unistd.h/read函数的包装
流程:
- 命令回复处理器
networking.c/sendReplyToClient函数为命令回复处理器,用于将服务器执行命令后得到的命令回复通过socket返回给客户端,具体实现为unistd.h/write函数的包装
流程:
- 一次完整的客户端与服务器连接事件示例
4.1.6 时间事件
分为定时事件和周期性事件:
时间事件主要属性:id、when、timeProc
一个时间事件是定时事件还是周期性事件取决于时间事件处理器的返回值:
4.1.6.1 实现
使用无序(不按when排序,按id排序)链表保存时间事件,每当时间事件执行器运行时,遍历整个链表,查找所有已到达的时间事件,并调用相应的事件处理器,复杂度为O(N)
例子:
注意:使用无序链表并不影响时间事件处理器的性能
4.1.6.2 API
4.1.6.3 时间事件应用实例:serverCron函数
执行定期操作,检查和调整自身的资源和状态,确保服务器可以长期、稳定地运行
主要工作:
4.1.7 事件的调度与执行
ae.c/aeProcessEvents函数:
将aeProcessEvents函数置于循环内处理:
事件角度下的服务器运行流程:
注意:事件之间不会出现抢占
一次完整的事件调度和执行过程: