Redis事件循环
- 文件事件
- 时间事件
- 事件调度和执行
- 客户端部分
- 关于客户端输出缓冲区限制
- ServerCron周期函数
- 服务器启动流程
- 小结
Redis服务器是一个事件驱动程序, 主要处理两类事件:
- 文件事件 (File Event) : 对套接字操作的抽象,服务器与客户端的通信过程会产生相应的文件实际,服务器通过监听并处理这些事件完成一些了网络通信操作。
- 时间事件 (Time Event): 需要定时或者周期性执行的操作,主要是redis中的serverCron函数。
我们需要关注的点有以下几处:
- redis如何处理文件事件,即: 处理客户端连接—>处理客户端命令—>回复客户端这个过程是怎样实现的
- redis时间事件作用,即: 周期性执行的函数serverCron干了啥
- redis如何在单线程的情况下统筹安排文件事件和时间事件的执行
文件事件
Redis基于IO多路复用模式开发了自己的网络事件处理器,这个处理器被称为文件事件处理器,该处理器核心运作流程如下:
- redis服务器初始化时会通过epoll监听server socket上产生的AE_READABLE事件,其实就是客户端accept事件,并且将连接应答处理器和客户端accpet事件关联起来,当产生accpet事件时,就触发关联的应答处理器执行。
- 当产生accpet事件时,会触发连接应答处理器执行,该处理器内部再获取到客户端的socket后,会向epoll注册监听当前socket的AE_READABLE事件,也就是监听客户端是否发命令了,并且会将客户端可读事件与命令请求处理器进行关联,该处理器负责处理客户端发送的命令。
- 当客户端发送一条命令请求后,对应客户端socket产生读事件,引发关联的命令请求处理器执行,该处理器从客户端socket中读取出命令请求内容,然后解析执行命令。
- 执行命令会产生对应的命令回复,为了将回复传送会客户端,服务器会将客户端socket的AE_WRITABLE可写事件与命令回复处理器进行关联,当对应的socket可写时,命令回复处理器将命令回复全部写到对应的客户端socket后,服务器就会解除对应客户端socket的AE_WRITABLE事件与命令回复处理器之间的关联。
具体细节参考本文
时间事件
一个时间事件主要由以下三个属性组成:
- id : 递增
- when : 时间事件到达时间
- timeProc : 时间事件处理器,当时间事件到达时,服务器调用绑定的处理器来处理事件
redis处理时间事件的流程大致如下所示 :
Tips:
如果时间事件处理器返回一个非AE_NOMORE的整数值,那么表示当前事件是一个周期性事件,返回值代表多少毫秒后该事件需要继续被执行。
Redis默认只会运行很少的时间事件,最重要的一个时间事件就是serverCron函数,该函数主要负责以下工作:
- 更新服务器各类统计信息,如时间,内存,数据库占用情况等
- 清理数据库中的过期键值对
- 关闭和清理连接失效的客户端
- 尝试进行AOF或RDB持久化操作
- 如果服务器是主服务器,那么对从服务器进行定期同步
- 如果处于集群模式,对集群进行定期同步和连接测试
Redis服务器会以周期性事件的方式来运行serverCron函数,该函数在Redis 2.6版本中默认每秒运行10次。
事件调度和执行
Redis是如何协调文件事件和时间事件的执行的呢?
由于redis需要不断的去处理文件和时间事件,因此aeProcessEvents函数需要置于一个循环里面,加上初始化和清理函数,这就构成了Redis服务器的主函数:
Redis事件循环机制的核心流程图如下所示:
客户端部分
redis是如何管理连接上来的多个客户端的呢?
具体看下图所示:
关于客户端输出缓冲区限制
服务器使用两种模式来限制客户端输出缓冲区大小:
- 硬性限制: 如果输出缓冲区大小超过了硬性限制设置的大小,那么服务器立刻关闭客户端
- 软性限制:如果输出缓冲区的大小超过了软性限制所设置的大小,但还没超过硬性限制,那么服务器将使用客户端状态结构的obuf_soft_limit_reached_time 属性记录下客户端到达软性限制的起始时间;之后服务器会继续监视客户端,如果输出缓冲区的大小一直超出软性限制,并且持续时间超过服务器设定的时长,那么服务器将关闭客户端;相反地,如果输出缓冲区的大小在指定时间之内,不再超出软性限制,那么客户端就不会被关闭,并且obuf_soft_limit_reached_time属性值会被清零。
使用client-output-buffer-limit选项可以为普通客户端、从服务器客户端、 执行发布与订阅功能的客户端分别设置不同的软性限制和硬性限制,该选项的格式为:
client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
以下是三个设置示例:
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
第一行设置将普通客户端的硬性限制和软性限制都设置为0,表示不限制客户端的输出缓冲区大小。
第二行设置将从服务器客户端的硬性限制设置为256MB,而软性限制设置为64MB,软性限制的时长为60秒。
第三行设置将执行发布与订阅功能的客户端的硬性限制设置为32MB,软性限制设置为8 MB,软性限制的时长为60秒。
ServerCron周期函数
serverCron函数默认每隔100毫秒执行一次,这个函数负责管理服务器的资源,并保持服务器自身良好运转,该周期函数每次运行时主要做了下面这些事情:
- 更新服务器时间缓存和LRU时钟
- 更新服务器每秒执行命令次数
- 更新服务器内存峰值记录
- 处理SIGETERM信号,通过注册该信号的处理函数,可以在redis关闭前进行RDB持久化工作
- 定期检查一定数量的客户端连接
- 管理数据库资源,例如: 删除过期键,收缩字典
- 执行被延迟的bgrewriteaof
- 检查持久化操作运行状态
- aof缓冲区判断是否需要刷入文件
服务器启动流程
- 初始化服务器状态
- 载入服务器配置
- 初始化服务器数据结构
- 还原数据库状态(优先选择aof)
- 执行事件循环
小结
本文主要参考 << Redis 设计与实现 >> 第十二章到第十四章内容。
细节可以看书,或者通过下面的文章进行学习:
Redis(五):Redis的事件循环
socket可读可写条件
Redis原理篇之网络模型
Redis 中的事件循环
Redis多线程网络模型全面解密