redis服务器是一个事件驱动程序。
需要处理两类事件:
1)文件事件:redis是通过套接字与客户端或者其他服务器连接的,而文件事件就是服务器对套接字操作的抽象。
2)时间事件:服务器对一些定时操作的抽象。
文件事件
redis基于reactor模式开发了自己的网络事件处理器,这个处理器被称作文件事件处理器,它使用IO多路复用程序来同时监听多个套接字, 并根据套接字目前执行的任务来为套接字关联不同的事件处理器,当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时, 与操作相对应的文件事件就会产生, 这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
虽然文件事件处理器以单线程方式运行, 但通过使用 I/O 多路复用程序来监听多个套接字, 文件事件处理器既实现了高性能的网络通信模型, 又可以很好地与 Redis 服务器中其他同样以单线程方式运行的模块进行对接, 这保持了 Redis 内部单线程设计的简单性。
文件事件处理器的构成:
I/O 多路复用程序负责监听多个套接字, 并向文件事件分派器传送那些产生了事件的套接字。
I/O 多路复用程序会把所有产生事件的套接字放到一个队列, 以有序(sequentially)、同步(synchronously)、每次一个套接字的方式,向文件事件分派器传送套接字。
I/O 多路复用程序可以监听多个套接字的 ae.h/AE_READABLE
事件和 ae.h/AE_WRITABLE
事件
1)当套接字变得可读时(客户端对套接字执行 write
操作,或者执行 close
操作), 或者有新的可应答(acceptable)套接字出现时(客户端对服务器的监听套接字执行 connect
操作), 套接字产生 AE_READABLE
事件。
2)当套接字变得可写时(客户端对套接字执行 read
操作), 套接字产生 AE_WRITABLE
事件。
如果一个套接字又可读又可写的话, 那么服务器将先读套接字, 后写套接字。
下面介绍各种处理器:
1)连接应答处理器:服务器进行初始化时, 程序会将连接应答处理器和服务器监听套接字的 AE_READABLE
事件关联, 当有客户端连接(connect
)服务器监听套接字的时候, 套接字就会产生 AE_READABLE
事件, 引发连接应答处理器执行, 并执行相应的套接字应答操作。
2)命令请求处理器:客户端连接到服务器后, 服务器会将客户端套接字的 AE_READABLE
事件和命令请求处理器关联起来, 当客户端发送命令请求时, 套接字就会产生 AE_READABLE
事件, 引发命令请求处理器执行, 并执行相应的套接字读入操作
3)命令回复处理器:服务器有命令回复需要传送给客户端, 服务器会将客户端套接字的 AE_WRITABLE
事件和命令回复处理器关联起来, 当客户端准备好接收服务器传回的命令回复时, 就会产生 AE_WRITABLE
事件, 引发命令回复处理器执行, 并执行相应的套接字写入操作。
一次完整的连接事件实例:
时间事件
redis时间事件可以分为两类:定时事件、周期性事件,他们的特点就像他们的名字一样。
而一个时间事件主要有三部分:
id:服务器为时间事件创建的全局唯一id,按时间递增,越新的越大
when:unix时间戳,记录到达时间
timeProc:时间事件处理器,是一个函数,时间事件到达时,服务器就会调用处理器来处理事件。
目前版本的redis只使用周期性事件
来看看实现:
服务器把所有时间事件放在一个链表中,每当时间事件执行器执行时,它就遍历链表,调用相应的事件处理器。
但是注意:链表是无序的,不按when属性来排序,当时间事件执行器运行时,必须遍历整个链表。但是,无序链表并不影响时间事件处理器的性能,因为在目前版本中,redis服务器只使用serverCron一个时间事件,就算在benchmark模式下也只有两个事件,服务器几乎是把链表退化成指针使用了。
事件的调度和执行
文件事件和时间事件之间是合作关系, 服务器会轮流处理这两种事件,对两种事件的处理都是同步、有序、原子地进行的,处理事件的过程中也不会进行抢占,所以时间事件的实际处理时间通常会比设定的到达时间晚一些。
大概流程为:
是否关闭服务器?---->等待文件事件产生---->处理已经产生的文件事件---->处理已经达到的时间事件---->是否关闭服务器?........