Skip to content

Latest commit

 

History

History
152 lines (95 loc) · 6.59 KB

ch12.md

File metadata and controls

152 lines (95 loc) · 6.59 KB

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

  • 文件事件(file event):Redis服务器通过socket与客户端连接,文件事件就是对套接字操作的对象。服务器与客户端的通信会产生相应的文件事件,服务器监听并处理这些事件来完成一系列的网络通信操作。
  • 时间事件(time event):Redis服务器的一些操作(如serverCron函数)需要在特定时间点执行,时间事件就是对这类定时任务的抽象。

12. 1 文件事件

Redis基于Reactor模式开发了自己的网络事件处理器,称为『文件事件处理器』,文件事件处理器以单线程方式运行。

文件事件处理器的四个组成部分:

  • 套接字。

    当被监听的套接字准备好执行accept、read、write、close等操作时,与操作相对应的文件事件就会产生。

  • I/O多路复用程序。

    使用I/O多路复用程序同时监听多个套接字,并向文件分派器传送那些产生了事件的套接字(使用队列)。

  • 文件事件分派器

    根据套接字的事件类型,调用相应的事件处理器。

  • 事件处理器

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

Redis的I/O多路复用包装了常见的select、poll、evport和kqueue等函数库来实现的,每个函数库的在Redis源码中都有一个独立的文件。

事件的类型

I/O多路复用程序可以监听多个套接字的ae.h/AE_READABLE和ae.h/AE_WRITABLE事件。两种事件可以同时监听,但会优先处理AE_READABLE事件。

API

函数 参数 作用
ae.c/aeCreateFileEvent 套接字描述符、事件类型、事件处理器 将给定套接字的给定事件加入到I/O多路复用程序的监听范围之内,并对事件和事件处理器进行关联
ae.c/aeDeleteFileEvent 套接字描述符、监听事件类型 让I/O多路复用程序取消套接字的事件监听,并取消事件与事件处理器的关联
ae.c/aeGetFileEvent 套接字描述符 返回套接字正在被监听的事件类型,none、readable、writable
ae.c/aeWait 套接字描述符、事件类型、毫秒数 在给定时间内阻塞并等待套接字的给定类型的事件发生
ae.c/aeApiPoll timeval结构 在给定事件内,阻塞并等待所有被acCreateFileEvent函数设置为监听状态的套接字产生文件事件
ae.c/aeProcessEvents 先调用aeApiPoll来等待事件,然后遍历所有事件,调用相应的事件处理器
ae.c/aeGetApiName 返回I/O多路复用程序底层使用的函数库名称:epoll、select等

文件事件的处理器

Redis为文件事件编写了多个处理器,分别用于实现不同的网络通信需求:

  • 连接应答处理器:监听客户端的套接字,并应答。

    networking.c/acceptTcpHandler函数,具体实现为sys/socket.h/accept函数的包装。服务器初始化时,会将这个处理器与套接字的AE_READABLE事件关联起来。

  • 命令请求处理器:接受来自客户端的命令请求。

    networking.c/readQueryFromClient函数,具体实现为unistd.h/read函数的包装。

  • 命令回复处理器:向客户端返回命令的执行结果。

    networking.c/sendReplyToClient函数,具体实现为unistd.h/write函数的包装。

  • 复制处理器:主从服务器的复制操作。

12.2 时间事件

Redis的时间事件分为两类:

  • 定时事件:在指定一段时间后执行一次。
  • 周期性事件:每隔一段时间就执行一次。

时间事件主要有三个属性:

  • id
  • when:毫秒进度的UNIX时间戳,事件的到达时间。
  • timeProc:时间事件处理器,事件到达时,负责处理事件。

一个事件是定时事件还是周期性事件,取决于时间事件处理器的返回值:

  • 返回ae.h/AE_NOMORE就是定时事件,到达一次后就删除
  • 返回非AE_NOMORE的整数值就是周期性事件,事件到达后,根据返回值对when属性进行更新。

实现

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

API

函数 参数 作用
ae.c/aeCreateTimeEvent 毫秒数,时间事件处理器 将新的时间事件添加到服务器
ae.c/aeDeleteFileEvent 事件ID 删除时间事件
ae.c/aeSearchNearestTimer 返回到达时间最近的事件
ae.c/processTimeEvents 时间事件的执行器,遍历并调用事件

serverCron函数

serverCron函数的工作包括:

  • 更新服务器的统计信息,如时间、内存占用、数据库占用
  • 清理过期的键值对
  • 关闭和清理失效的连接
  • 尝试AOF或RDB持久化
  • 如果是主服务器,对从服务器定期同步
  • 如果是集群模式,对集群进行同步和测试连接

12.3 事件的调度与执行

调度和执行由ae.c/aeProcessEvents函数负责。

def aeProcessEvents():
    # 获取最近的事件
    time_event = aeSearchNearestTimer()
    
    # 计算最近的事件还有多少毫秒
    remaind_ms = time_event.when - unix_ts_now()
    
    # 如果事件已到达
    if remaind_ms < 0:
        remaind_ms = 0
        
    # 根据remaind_ms的值,创建timeval结构
    timeval = create_timeval_with_ms(remaind_ms)
    
    # 阻塞并等待文件事件
    # 如果remaind_ms为0,那么aeApiPoll调用之后马上返回,不阻塞
    aeApiPoll(timeval)
    
    # 处理所有已产生的文件事件
    processFileEvents()
    
    # 处理所有已到达的时间事件
    processTimeEvents()
    

调度和执行的规则如下:

  • aeApiPoll函数的最大阻塞时间由到达时间最接近当前时间的事件决定,避免服务器的频繁轮询。
  • 如果处理完一次文件事件后,未有时间事件到达,则再次处理文件事件。
  • 对事件的处理都是同步、有序、原子地执行。不会中断、抢占事件处理。
  • 时间事件的处理时间,通常比其设定的到达时间晚一些。

导航

目录

上一章:11. AOF持久化

下一章:13. 客户端