Redis:一个靠事件驱动的程序
Redis:一个靠事件驱动的程序
HbzhtdRedis服务器本身其实是一个事件驱动程序,内部的所有操作基本都是靠事件进行驱动,也可以说,每一个操作都是一个事件。
Redis中的事件分为两种,文件事件和时间事件。
文件事件
首先说文件事件,文件事件是套接字操作的抽象,套接字是Redis服务器和客户端连接和通信的桥梁,在通信的过程中,会产生相应的文件事件,服务器针对这些事件进行监听和处理,就可以完成一系列网络通信操作。
其次是时间事件,Redis中的一些操作,需要在特定时间点下进行,时间事件就是这些操作的抽象。
Redis基于Reactor模式,开发了自己的网络事件处理器,也称为文件事件处理器。文件事件处理器使用I/O多路复用模型来同时监听多个套接字,并根据套接字的类型,分配不同的事件处理器(accept、read、write、close等),当事件发生时,就通过分配的处理器进行处理。
文件事件处理器本身是单线程模型,通过使用I/O多路复用模型,既实现了高性能,又能和其他同样是单线程运行的模块进行对接,保持了单线程设计的简单性。
文件事件处理器由四个部分组成:套接字,I/O多路复用程序,文件事件分配器,事件处理器。
I/O多路复用程序会监听多个套接字,当套接字准备好执行 连接应答( accept )、写入、读取关闭等操作时,I/O多路复用程序就会将这些套接字放到一个队列里面,每次会从队列中拿取一个发给文件事件分配器,只有当拿取的这个事件执行完毕,才会发送下一个队列里的事件。
套接字生成的事件,还可以归为两类,可读事件和可写事件。
可读事件包括:客户端对套接字执行 write 操作、close 操作时,或客户端对服务器的监听套接字执行 connect 操作。
可写事件包括:客户端对套接字执行 read 操作。
如果一个套接字同时产生这两种事件,也就是这个套接字既可读又可写,那么服务器优先读,后写套接字。
时间事件
Redis当中的时间事件可以分为两种,定时事件和周期性事件。
定时事件是让程序在特定时间点执行一次。周期性事件是让程序每隔一定时间执行一次。
一个时间事件由三个属性组成:ID(全局唯一,递增)、when(毫秒时间戳,事件的到达时间)、timeProc(时间事件处理器,当事件到达,服务器就会调用该处理器)
目前Redis服务器只实现了周期性事件(3.0版本),无定时事件。
服务器将所有的时间事件保存在无序链表中(头插法),当时间事件执行器执行时,需要遍历整个链表,找到已到达的时间事件,然后调用对应的事件处理器。
注意:链表会按照ID排序,但不会按照when排序,因此只能遍历。也因此能得出,事件开始执行时间>=事件到达时间。
Redis实现的周期性事件:serverCron 函数
持续运行的Redis服务器需要定期检查和调整自身的资源和状态信息,从而确保服务器可以长期稳定运行,这些操作就是由 serverCron 函数负责。主要工作包括:
- 更新服务器各类统计信息,比如时间、内存占用、数据库占用等。
- 清理数据库中的过期键(定期+惰性删除)。
- 关闭和清理连接失效的客户端。
- 尝试进行 AOF 和 RDB 持久化。
- 如果服务器是主服务器,那么对从服务器进行定期同步。
- 如果处于集群模式,对集群进行定期同步和连接测试。
事件的调度与执行
服务器对文件事件和时间事件的调度是同步、有序、原子性的,不存在中断和抢占操作。但是事件会尽量减少程序的阻塞时间,在有需要时主动让出执行权,从而降低事件饥饿的可能性。
参考
- 《Redis设计与实现》第12章.事件