前言

Redis自己封装了一个事件驱动模型,实现代码在src/ae.hsrc/ae.c。Redis内部存在两类事件:文件事件时间事件
其中文件事件包括网络事件、文件读写事件等;而时间事件主要是一些后台定时任务事件。

本文主要围绕这两个代码文件,对Redis的事件模型进行分析学习。

【不对Reactor模型,具体的事件处理流程等进行分析说明。这部分说明后续通过单独文章进行描述分析】

首先我们先看两个图片:
在这里插入图片描述

图片二</i>
在这里插入图片描述
图片1是基于ac.h代码抽象出来的Redis事件实现模型逻辑架构图。图片二是struct aeEventLoop结构图。

下面我们基于这两个图片进行分析说明:

事件模型实现逻辑

      Redis是单线程的,其事件模型是属于高性能事件模型中的Reactor模式,本文不对这个模式进行说明分析,具体需要了解这个模式的可以参考网上其他的文章。
     &nbps;通过图片的逻辑架构我们得知,Redis对于事件模型的封装是分层的。通过图片我们可以简单拆分为:

  • 事件模型应用层
  • 事件结构体层
  • 事件模型封装层&支撑层

    事件模型封装&支撑层

    这一层主要是对操作系统提供的各种IO多路复用模型进行统一封装,对上一层提供统一的访问接口。Redis在此封装了四种类型的IO多路复用模型select,epoll,evpoll,kequeue。对上层提供了以下的统一接口:

  • aeApiState

  • aeApiCreate
  • aeApiFree
  • aeApiAddEvent
  • aeApiDelEvent
  • aeApiPoll
  • aeApiName

具体使用哪种底层的IO模型,通过条件编译方式进行指定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//根据相关编译宏来选择当前系统匹配的最优事件模型
#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,aeTimeEventaeFiredEvent
      aeEventLoop结构体是事件循环结构体,整个事件的声明周期都围绕这一结构体展开。其内部定义参考以上第二个图片。
      aeFileEventaeTimeEvent两个结构体分别用于封装一个文件事件和时间事件。
      aeFiredEvent则用来封装就绪事件,每当系统出现一个就绪事件,都通过一个aeFiredEvent结构体进行封装。

事件模型应用层

      这一层提供了一些操作事件模型的API,Redis内部其他逻辑模块通过这些api进行事件模型的交互操作。
按照功能来划分主要是分以下几类:

  • 模型创建、删除、属性变更;
  • 模型操控:包括启动、停止、事件分发等
  • 文件事件操控:注册文件事件、删除文件事件、获取文件事件信息等
  • 时间事件操控:创建时间事件、删除时间事件
  • 获取当前使用的底层IO多路复用模型名称
  • 额外辅助:设置事件模型等待就绪事件前后间隙的回调函数。

事件模型结构体

      Redis是单进程程序,并且一般情况下,处理客户端交互的都是单线程(6.0以后增加了IO线程,但是主要业务逻辑还是单线程处理)。整个后台服务只有一个aeEventLoop结构体对象。
      通过以上图片的分析得知,这结构体内部分别通过连续内存数组和链表的方式分别对文件事件和时间事件进行了维护。
所有注册的文件模型都通过aeFileEvent *events进行维护,没增加一个文件事件都通过其文件描述符作为索引定位到该数组的具体槽位;当有文件事件就绪,则通过其文件描述符定位到该槽位获取对应的信息,进行事件处理操作。
      时间事件和文件不同,它通过了aeTimeEvent *timeEventHead这一链表来维护,时间事件结构体内部有前置、后驱指针可以串联节点。每当新增一个时间事件,就往链表增加一个节点即可。删除事件,则摘除节点。

      此外该结构体还存在其他成员变量,比如:stop事件循环停止标志,void* apidata事件循环额外数据指针,int maxfd当前在事件循环中最大的文件描述符是多少等等。

总结

      本文仅对事件模型实现代码进行简单分析说明,了解Redis代码层面是如何差异化不同的系统IO多路复用模型进行封装使用,以及相关事件是如何进行管理维护的。
      后续篇章会对Redis事件整个分发逻辑、事件种类进行更详细描述说明。

【本文同步发布于:https://blog.csdn.net/guanlq/article/details/123489661?spm=1001.2014.3001.5501】