前言
Redis自己封装了一个事件驱动模型,实现代码在src/ae.h
和src/ae.c
。Redis内部存在两类事件:文件事件
和时间事件
。
其中文件事件
包括网络事件、文件读写事件等;而时间事件
主要是一些后台定时任务事件。
本文主要围绕这两个代码文件,对Redis的事件模型进行分析学习。
【不对Reactor模型和具体的事件处理流程等进行分析说明。这部分说明后续通过单独文章进行描述分析】
首先我们先看两个图片:
图片二
图片1是基于ac.h
代码抽象出来的Redis事件实现模型逻辑架构图。图片二是struct aeEventLoop
结构图。
下面我们基于这两个图片进行分析说明:
事件模型实现逻辑
Redis是单线程的,其事件模型是属于高性能事件模型中的Reactor
模式,本文不对这个模式进行说明分析,具体需要了解这个模式的可以参考网上其他的文章。
通过图片的逻辑架构我们得知,Redis对于事件模型的封装是分层的。逻辑上是可以简单拆分为:
- 事件模型应用层
- 事件结构体层
- 事件模型封装层&支撑层
事件模型封装&支撑层
这一层主要是对操作系统提供的各种IO多路复用模型进行统一封装,对上一层提供统一的访问接口。Redis在此封装了四种类型的IO多路复用模型select
,epoll
,evpoll
,kequeue
。对上层提供了以下的统一接口:
- aeApiState
- aeApiCreate
- aeApiFree
- aeApiAddEvent
- aeApiDelEvent
- aeApiPoll
- aeApiName
每个底层分别实现这一套对外的接口。然后具体使用哪种底层的IO模型,是通过条件编译方式进行指定:
//根据相关编译宏来选择当前系统匹配的最优事件模型
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
#ifdef HAVE_EPOLL
#include "ae_epoll.c"
#else
#ifdef HAVE_KQUEUE
#include "ae_kqueue.c"
#else
#include "ae_select.c"
#endif
#endif
#endif
基于不同的操作系统进行编译,则不同的宏分被定义,基于此,把具体IO模型实现代码include进来、编译。
事件结构体层
这一层主要是定义了事件模型使用的相关结构体:aeEventLoop
,aeFileEvent
,aeTimeEvent
和aeFiredEvent
。
aeEventLoop
结构体是事件循环结构体,整个事件的生命周期都围绕这一结构体展开、它的一个实例保存在全局变量server
成员中。而该结构体内部定义则可参考以上第二个图片。
aeFileEvent
和aeTimeEvent
两个结构体分别用于封装一个文件事件和时间事件。
其中文件事件
是通过数组方式进行维护管理;时间事件
则通过双向链表结构进行维护管理。
aeFiredEvent
则用来封装就绪事件,每当系统出现一个就绪事件,通过调用事件模型封装&支撑层
提供的接口,把事件相关的信息保存在aeFiredEvent
结构体进行返回上层应用处理。
事件模型应用层
这一层提供了一些操作事件模型的API,Redis内部其他逻辑模块通过这些api进行事件模型的交互操作。
按照功能来划分主要是分以下几类:
- 模型创建、删除、属性变更;
- 模型操控:包括启动、停止、事件分发等
- 文件事件操控:注册文件事件、删除文件事件、获取文件事件信息等
- 时间事件操控:创建时间事件、删除时间事件
- 获取当前使用的底层IO多路复用模型名称
- 额外辅助:设置事件模型等待就绪事件前后间隙的回调函数。
事件模型结构体
Redis是单进程程序,并且一般情况下,处理客户端交互的都是单线程(6.0以后增加了IO线程,但是主要业务逻辑还是单线程处理)。整个后台服务只有一个aeEventLoop
结构体对象。
通过以上图片的分析得知,这结构体内部分别通过连续内存数组和链表的方式分别对文件事件和时间事件进行了维护。
所有注册的文件模型都通过aeFileEvent *events
进行维护,没增加一个文件事件都通过其文件描述符作为索引定位到该数组的具体槽位;当有文件事件就绪,则通过其文件描述符定位到该槽位获取对应的信息,进行事件处理操作。
时间事件和文件不同,它通过了aeTimeEvent *timeEventHead
这一链表来维护,时间事件结构体内部有前置、后驱指针可以串联节点。每当新增一个时间事件,就往链表增加一个节点即可。删除事件,则摘除节点。
此外该结构体还存在其他成员变量,比如:stop
事件循环停止标志,void* apidata
事件循环额外数据指针,int maxfd
当前在事件循环中最大的文件描述符是多少等等。
总结
本文仅对事件模型实现代码进行简单分析说明,了解Redis代码层面是如何差异化不同的系统IO多路复用模型进行封装使用,以及相关事件是如何进行管理维护的。
后续篇章会对Redis事件整个分发逻辑、事件种类进行更详细描述说明。