欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Redis(源码剖析):30---事件之服务器的事件调度与执行(aeProcessEvents函数)

程序员文章站 2022-03-10 10:24:25
...

一、服务器事件的调度与执行

  • 因为服务器中同时存在文件事件和时间事件两种事件类型,所以服务器必须对这两种事件进行调度,决定何时应该处理文件事件,何时又应该处理时间事件,以及花多少时间来处 理它们等等

二、aeProcessEvents函数

  • 事件的调度和执行由ae.c/aeProcessEvents函数负责
  • 以下是该函数的伪代码表示:
    • 备注:在实际中,处理已产生文件事件的代码是直接写在aeProcessEvents函数里面 的,这里为了方便讲述,才虚构了processFileEvents函数
def aeProcessEvents():
    # 获取到达时间离当前时间最接近的时间事件
    time_event = aeSearchNearestTimer()

    # 计算最接近的时间事件距离到达还有多少毫秒
    remaind_ms = time_event.when - unix_ts_now()

    # 如果事件已到达,那么remaind_ms 的值可能为负数,将它设定为0
    if remaind_ms < 0:
        remaind_ms = 0

    # 根据remaind_ms 的值,创建timeval 结构
    timeval = create_timeval_with_ms(remaind_ms)

    # 阻塞并等待文件事件产生,最大阻塞时间由传入的timeval 结构决定
    # 如果remaind_ms 的值为0 ,那么aeApiPoll 调用之后马上返回,不阻塞
    aeApiPoll(timeval)

    # 处理所有已产生的文件事件
    processFileEvents()

    # 处理所有已到达的时间事件
    processTimeEvents()

三、服务器的事件循环

  • 将aeProcessEvents函数置于一个循环里面,加上初始化和清理函数,这就构成了Redis服务器的主函数,以下是该函数的伪代码表示:
def main():
    # 初始化服务器
    init_server()
    
    # 一直处理事件,直到服务器关闭为止
    while server_is_not_shutdown():
        aeProcessEvents()
    
    # 服务器关闭,执行清理操作
    clean_server()

图解

  • 从事件处理的角度来看,Redis服务器的运行流程可以用流程图来表示:

Redis(源码剖析):30---事件之服务器的事件调度与执行(aeProcessEvents函数)

以下是事件的调度和执行规则:

  • ①aeApiPoll函数的最大阻塞时间由到达时间最接近当前时间的时间事件决定,这个方 法既可以避免服务器对时间事件进行频繁的轮询(忙等待),也可以确保aeApiPoll函数不会 阻塞过长时间
  • ②因为文件事件是随机出现的,如果等待并处理完一次文件事件之后,仍未有任何时 间事件到达,那么服务器将再次等待并处理文件事件。随着文件事件的不断执行,时间会逐 渐向时间事件所设置的到达时间逼近,并最终来到到达时间,这时服务器就可以开始处理到 达的时间事件了
  • ③对文件事件和时间事件的处理都是同步、有序、原子地执行的,服务器不会中途中 断事件处理,也不会对事件进行抢占,因此,不管是文件事件的处理器,还是时间事件的处 理器,它们都会尽可地减少程序的阻塞时间,并在有需要时主动让出执行权,从而降低造成 事件饥饿的可能性。比如说,在命令回复处理器将一个命令回复写入到客户端套接字时,如 果写入字节数超过了一个预设常量的话,命令回复处理器就会主动用break跳出写入循环,将 余下的数据留到下次再写;另外,时间事件也会将非常耗时的持久化操作放到子线程或者子 进程执行
  • ④因为时间事件在文件事件之后执行,并且事件之间不会出现抢占,所以时间事件的 实际处理时间,通常会比时间事件设定的到达时间稍晚一些

演示案例

  • 下表记录了一次完整的事件调度和执行过程:

Redis(源码剖析):30---事件之服务器的事件调度与执行(aeProcessEvents函数)

  • 上表记录的事件执行过程凸显了上面列举的事件调度规则中的规则②、③、④:
    • 因为时间事件尚未到达,所以在处理时间事件之前,服务器已经等待并处理了两次文件 事件
    • 因为处理事件的过程中不会出现抢占,所以实际处理时间事件的时间比预定的100毫秒 慢了30毫秒