解析Redis源码设计剖析之事件处理

1. Redis事件处理简介

Redis是一款高性能、内存型的NoSQL数据库,Redis主要采用了事件驱动机制,Redis事件所涉及的文件主要有server.cae.cae_kqueue.cae_epoll.c等。

Redis事件包括:

文件事件

时间事件

信号事件

其中最常用的就是文件事件类型,Redis事件处理依托于Redis的多路I/O复用,通过调用I/O多路复用底层库,如:libev、libevent、libuv等实现了非阻塞I/O操作,Redis将底层复杂的文件描述符(如套接字)上发生的I/O事件都统一转换为事件处理器

eventLoop

进行处理。

2. Redis事件处理模型

Redis事件处理模型的核心就是事件循环处理器,Redis的事件处理器是单独的一个事件处理循环,如果有任何事件发生,Redis调用事件处理器处理事件。

由于Redis是单线程的,所以事件处理中需要注意不要阻塞事件循环,如果事件处理过程中有操作阻塞了事件循环,那么这个时间循环就无法去处理别的事件,这时候Redis将不能及时响应客户端的命令请求,导致客户端响应变慢或直接timeout。所以Redis事件处理循环中不能调用太多耗时的操作,那么Redis的事件循环该如何处理呢?

2.1 事件循环处理器

Redis通过一个统一的事件循环处理器——eventLoop来实现事件的处理,eventLoop的定义如下:

struct aeEventLoop {

int maxfd; /* 当前注册的最大fd */

int setsize; /* events数组的大小 */

long long timeEventNextId;

time_t lastTime; /* 前一个运行时间 */

aeFileEvent *events; /* events数组,同时保存了文件事件和信号事件 */

aeFiredEvent *fired; /* fired数组 */

aeTimeEvent *timeEventHead; /* 时间事件链表头 */

int stop; /* 是否停止事件循环,0不停止,1停止 */

void *apidata; /* 用于参数透传给多路io复用库的私有结构体 */

aeBeforeSleepProc *beforesleep; /* 在事件loop sleep期前执行的函数 */

};

Redis的事件循环处理器通过一个定时器和多态I/O复用系统底层库实现:

Redis事件循环使用基于链表的时间事件处理机制,来定期执行特定的回调函数,eventLoop会把所有注册的时间事件组成一个链表每次寻找链表中符合调用时刻的事件来执行。

Redis事件循环通过多态I/O复用系统库来监听多个文件描述符(socket、管道、文件)的可读可写等事件,当socket可读或可写事件发生之后,就会调用该socket的事件处理器。

Redis通过将事件处理器注册到eventLoop的文件事件表里,并维护文件描述符的状态,来处理文件事件。

2.2 关键操作hwere

2.2.1 文件事件的处理

Redis的文件事件由eventLoop结构体中的events数组来维护,结构体aeFileEvent则用来保存所有可以处理的事件。

typedef struct aeFileEvent {

int mask; /* 处理事件类型掩码 */

aeFileProc *rfileProc; /* 可读事件处理器 */

aeFileProc *wfileProc; /* 可写事件处理器 */

void *clientData; /* 客户端数据 */

} aeFileEvent;

Redis中事件的处理由文件事件的eventLoop和底层多路I/O复用系统库完成,如select、poll、epoll等。

Redis采用I/O多路复用技术来监听socket读/写事件,如果有读/写事件发生,则将该读/写事件放入到fd队列中,底层等待到对应文件描述符变更事件后,就开始处理这些文件描述符,这样极大的节省了CPU的资源。

2.2.2 时间事件的处理

Redis使用基于链表的时间事件处理机制,将所有的时间事件链接成一个链表,每个节点保存下次调度的绝对时间和处理函数。

typedef struct aeTimeEvent {

long long id; /* 时间事件ID */

long when_sec; /* 时间事件触发秒数 */

long when_ms; /* 时间事件触发毫秒数 */

aeTimeProc *timeProc; /* 时间事件处理器函数 */

aeEventFinalizerProc *finalizerProc;

void *clientData; /* 处理器私有数据 */

struct aeTimeEvent *next; /* 下一个时间事件 */

} aeTimeEvent;

Redis中的时间事件以毫秒为单位,在处理子节点是否过期时,直接对比其触发时间是否小于当前Unix时间即可。

总结

本文主要介绍了 Redis 事件处理模型,并重点介绍 Redis 中处理事件的两种方法——文件事件和时间事件,以及 Redis 的事件循环处理器——eventLoop的定义和作用。通过本文的阅读,相信大家已经对 Redis 的事件处理机制有了一个更深入的了解,希望大家相互学习、共同进步!

数据库标签