• Redis篇(3)——事件循环与io模型


    ae

    redis是一种典型的reactor设计模式。redis中的任何操作甚至是定时任务的触发都被看成是一个事件,由专门的事件处理器去执行。其内部自己定义了一套ae驱动器ae.hae.c)。
    在这里插入图片描述
    事件分为文件事件时间事件。像命令的请求,回复都是文件事件。而时间事件则是一种定时触发的事件,如serverCron函数(周期时间事件)、或者过期键的删除(定时时间事件)等。

    服务器将所有的时间事件都放在了一个无序链表,每当时间事件执行器运行时,就会遍历整个链表,查找所有已到达的时间事件,并调用相应的事件处理器。定时时间事件在执行的时候会先判断当前时间是否到了,若还没到则会把事件重新加入时间事件链表等待下次的触发。而周期时间事件则是在执行之后会新建一个时间事件并且指定下次触发的时间加入到时间事件链表。

    其实无论是时间事件还是文件事件都会用链表去描述。具体见
    aeEventLoop
    在这里插入图片描述
    redis只需循环处理aeEventLoop上的事件即可。而这个循环的入口就是
    aeMain
    在这里插入图片描述
    上图不难看出,事件处理逻辑主要在aeProcessEvents
    在这里插入图片描述
    除aeMain和aeProcessEvents外,ae其他常用api如下

    api说明
    aeCreateEventLoop初始化一个事件循环结构体(eventLoop)
    aeGetSetSize返回当前setsize的值
    aeResizeSetSize改变setsize的值(重新分配空间)
    aeDeleteEventLoop删除事件循环,释放内存空间
    aeStop停止事件循环,即stop值设为1
    aeProcessEvents事件处理逻辑
    aeMain事件循环的入口
    aeSetBeforeSleepProc注册回调函数,每次主循环在休眠前被调用
    aeSetAfterSleepProc注册回调函数,每次主循环在休眠后被调用
    aeWait等待指定套接字指定事件的产生
    aeCreateFileEvent创建一个文件事件
    aeCreateTimeEvent创建一个时间事件

    io模型

    以客户端发送的命令为例,当套接字收到客服端发来的请求时,会调用aeCreateFileEvent创建一个文件事件并加入到aeEventLoop的事件链表中,由aeMain的事件循环中调用指定的事件处理器去处理该事件。
    那么问题来了,事件循环只有一个但客户端可以有多个。换句话说就是redis是如何做到在一个线程里面高效的处理多个客户端的请求的呢?其秘密武器就是多路复用的IO模型——NIO

    如下图所示redis内部对不同操作系统所能支持的nio模型做了不同的实现,但对外暴露的api是一致的。这么做的好处就是nio内部的实现对于调用方来说可以是黑盒,无需关心其内部的具体实现。
    在这里插入图片描述

    api描述
    aeApiCreate创建底层apidata(如:epoll_create)
    aeApiResize重新分配底层ae事件内存
    aeApiFree关闭底层ae并释放内存
    aeApiAddEvent发起一个事件
    aeApiDelEvent删除一个事件
    aeApiPoll查询发生的监听事件
    aeApiName返回实现类型(select/epoll/evport/kqueue)

    具体什么系统用什么实现,可以在ae.c找到答案
    在这里插入图片描述
    我们点进去开就能发现linux系统用的是epoll的实现。
    在这里插入图片描述
    说了这么多,那么到底aeMain和nio有什么关系呢? 其实aeMain是以nio的为基础的事件响应框架,而nio是aeMain的底层实现。
    以常用的epoll模型为例,aeCreateFileEvent方法调用了aeApiAddEvent。(aeApiAddEvent正是redis对nio实现所对外暴露的api之一)
    在这里插入图片描述
    点开aeApiAddEvent可以发现其内部调用的正是epoll_ctl这个系统函数
    在这里插入图片描述
    同理aeApiPoll调用的是epoll_wait
    在这里插入图片描述

    好了,那么问题又来了,为什么nio会高效呢?其实现高效的原理是什么?
    其实这个高效是操作系统自己实现的(毕竟调用的就是系统函数)。就以epoll为例,其内部的实现有几个关键字:零拷贝(共享内存空间) + 红黑树 + 链表有兴趣的可以去了解一下。

    最后再多说一句,除了redis的ae外,像netty、libevent也是一样基于nio实现的事件响应框架。有兴趣也可以去了解一下。

    参考文献:《Redis设计与实现》黄健宏著、redis-5.0.14源码

  • 相关阅读:
    激活函数与loss的梯度
    数据结构与算法 -- 动态规划基础
    C++递归函数
    东方梅酒:梅见的新国饮故事
    systemverilog学习 ---- event
    labview卸载重装碰到的问题
    Java通过http请求的方式调用他人的接口
    计算机网络——23网络层导论
    温敏性N-异丙基丙烯酰胺(NIPA)和pH敏感性丙烯酸(AA)接枝纳米聚苯乙烯微球相关研究
    被领导夸奖的终极目标计划表,让你人生开挂
  • 原文地址:https://blog.csdn.net/qq_43196360/article/details/127607794