zl程序教程

您现在的位置是:首页 >  .Net

当前栏目

Redis事件循环

2023-02-18 16:49:14 时间

Redis事件循环


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的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 设计与实现 >> 第十二章到第十四章内容。