Redis入门8-事件

Redis服务器是一个事件驱动程序,服务器需要处理以下两类事件:

  1. 文件事件(file event):Redis 服务器通过套接字与客户端(或其他Redis服务器)进行连接、而文件事件就是服务器对套接字操作的抽象。服务器通过监听并处理这些事件来完成一些列网络通信操作
  2. 时间事件(time event):Redis 服务器中的一些操作(比如 serverCron函数) 需要在给定的时间点执行,而时间事件就是服务器对这类定时操作的抽象

文件事件

Redis 基于 Reactor 模式开发了自己的网络事件处理器:这个处理器被称为文件处理器(file event handler)

  • 文件事件处理器使用 I/O多路复用(multiplexing)程序来同时坚挺多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器
  • 当被监听的套接字准备号执行链接应答、读取、写入、关闭等操作时,与操作相对应的文件事件就会产生,这时文件时间处理器就会调用套接字之前关联号的事件处理器来处理这些事件
image-20211010214336313

文件事件处理器的构成

文件事件处理器由四部分组成,分别是:套接字、I/O多路复用程序、文件事件分派器(dispatcher)、事件处理器

I/O多路复用程序负责监听多个套接字、并向文件事件分派器传送那些产生了事件的套接字。

尽快多个文件事件可能会并发地出现。但I/O多路复用程序会将所有产生的事件的套接字多放到一个队列里面,通过这个队列,有序同步每次一个套接字的方式向文件事件分派器传送套接字,当上一个套接字产生的事件被处理完毕之后(该套接字为事件锁关联的事件处理器执行完毕),I/O多路复用程序才会继续向文件事件分派器传送下一个套接字

image-20211010214918846

这些处理器其实就是一个个的函数,一次Redis客户端与服务器进行连接并发送命令的过程就是

  • 客户端向服务端发起建立 socket 连接的请求,那么监听套接字将产生 AE_READABLE 事件,触发连接应答处理器执行。处理器会对客户端的连接请求

  • 进行应答,然后创建客户端套接字,以及客户端状态,并将客户端套接字的 AE_READABLE 事件与命令请求处理器关联。

  • 客户端建立连接后,向服务器发送命令,那么客户端套接字将产生 AE_READABLE 事件,触发命令请求处理器执行,处理器读取客户端命令,然后传递给相关程序去执行。

  • 执行命令获得相应的命令回复,为了将命令回复传递给客户端,服务器将客户端套接字的 AE_WRITEABLE 事件与命令回复处理器关联。当客户端试图读取命令回复时,客户端套接字产生 AE_WRITEABLE 事件,触发命令回复处理器将命令回复全部写入到套接字中。

I/O多路复用程序的实现

Redis 的I/O多路复用程序的所有功能都是通过包装常见的select、epoll、evport和kqueue这些I/O多路复用函数库来实现的,每个I/O多路复用函数库在Redis源码中读对应一个单独的文件,比如ae_select.c、ae_epoll.c、ae_kqueue.c。诸如此类

为每个I/O多路复用函数库都实现了相同的api,所以I/O多路复用程序底层实现是可以互换的

image-20211010215137555

Redis 线程不会阻塞在某一个特定的监听或已连接套接字上,Redis 可以同时和多个客户端连接并处理请求,从而提升并发性。

img

select/epoll 一旦监测到 FD 上有请求到达时,就会触发相应的事件。这些事件会被放进一个事件队列,Redis 单线程对该事件队列不断进行处理。这样一来,Redis 无需一直轮询是否有请求实际发生,这就可以避免造成 CPU 资源浪费。同时,Redis 在对事件队列中的事件进行处理时,会调用相应的处理函数,这就实现了基于事件的回调。

类似于 大家去医院一起挂号,登记,然后一起排队等待医生的处理

时间事件

服务器将所有时间事件都放在一个无序链表(不按when属性的大小排序)中,每当时间时间执行器运行时,它就遍历整个链表,查找所有已达到的时间事件,并调用相应的事件处理器

image-20211010220322769

时间事件应用实例 redis.c/serverCron 函数,主要工作有

  1. 更新服务器的各类统计信息,比如时间、内存占用、数据库占用情况
  2. 清理数据库中的过期键值对
  3. 关闭和清理连接失效的客户端
  4. 尝试执行AOF或RDB持久化操作
  5. 如果服务器是主服务器,那么对从服务器进行定期同步
  6. 如果处理集群模式,对集群进行定期同步和连接测试

事件的调度与执行

服务器同时存在文件事件和时间时间两种事件类型,所以服务器必须同时这两种事件进行调度,

  • 决定何时应该处理文件时间,何时又应该处理时间时间
  • 需要花多长时间来处理事件

事件的调度和执行由 ae.c/aeProcessEvents 函数负责

1
2
3
4
5
6
7
8
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
aeProcessEvents(eventLoop, AE_ALL_EVENTS|
AE_CALL_BEFORE_SLEEP|
AE_CALL_AFTER_SLEEP);
}
}
image-20211010221320033

参考文献

  1. 《Redis 设计与实现》