由于老大在新项目中使用redis的事件库代替了libevent,我也趁着机会读了一遍redis的事件库代码,第一次读到“优美,让人愉快”的代码,加之用xmind制作的类图非常帅,所以留文纪念。

Redis的事件库主要集中在ae.h和ae.c中,此外还有ae_epoll.c等底层实现文件,根据系统可选择事件库的实现方式,典型的讲上层实现与底层实现分离的结构。

ae.h是事件库函数的定义与使用的结构体,关于结构体的相互联系可参见类图。

除了事件库,Redis还封装了malloc等内存管理函数,为标准函数增加了一个记录占用内存量的部分,如果想独立使用redis的事件库,只要把ae.c中的zmalloc全部替换成标准malloc即可。

下面开始介绍ae.c,一段典型的ae事件库如下图所示:

int main()
{
aeEventLoop el;
el = aeCreateEventLoop();
aeCreateFileEvent(el, fd, mask, proc, clientData);
aeCreateTimeEvent(el, milliseconds, proc, clientData, finalizerProc);
aeMain(el);
}

aeCreateEventLoop用于对ae事件库进行初始化,然后通过aeCreateFileEvent和aeCreateTImeEvent可以添加需要监控的fd读写事件或者定时事件,通过类图,可以看到对于fd读写时间有两个钩子,一个是读事件的钩子,一个是写时间的钩子;对于定时事件也有两个钩子,一个是定时时间到达时的钩子,一个是delete定时事件时执行的钩子。

创建完我们需要的事件后,调用aeMain进入事件驱动。aeMain中,判断几个执行变量即开始死循环调用aeProcessEvents处理事件。

aeProcessEvents首先需要从时间事件中寻找最近要执行的定时事件时间,此时间与当前时间的间隔即是调用aeApiPoll的超时时间,避免因epoll超时,影响定时事件执行。aeApiPoll会将需要处理的fd事件,放入fired队列,因此调用aeApiPoll之后,是通过遍历fired队列来执行fd事件的。fd事件处理结束,调用processTimeEvents处理定时事件,processTimeEvents每执行一个定时事件都要重新遍历定时事件队列,因为每个定时事件的执行,都会导致当前时间改变,从而有新的事件需要执行,但是processTimeEvents不会处理新加入的时间事件,以免该循环永不退出(但是目前还是有可能因为定时事件执行时间过长而导致循环永不退出)。到此整个ae事件驱动的核心就结束了。

需要注意的是,定时事件的钩子函数需要返回定时事件的时间间隔,或者返回AE_NOMORE表示删除该定时事件,实际上这也允许了我们动态调整定时事件的时间间隔。

Ae时间驱动,可以优化的地方很多,在代码注释中,开发人员解释道,因为redis对事件驱动的使用并不复杂,因此没有进行更多的优化,例如ae的定时事件队列实现上就是一个队列,因此寻找最近要执行的定时事件需要O(n)的复杂度,因为这个问题,执行定时事件时,也需要不断重复遍历定时队列,查找要执行的事件,我想最普遍的想法就是将这个队列实现为堆,或者二叉树,但是redis里建议实现为跳表,跳表的算法我看过一次,没发现特殊的性能优势,有时间还要研究研究。

Redis事件库源码分析的更多相关文章

  1. Redis网络库源码分析(1)之介绍篇

    一.前言 Redis网络库是一个单线程EPOLL模型的网络库,和Memcached使用的libevent相比,它没有那么庞大,代码一共2000多行,因此比较容易分析.其实网上已经有非常多有关这个网络库 ...

  2. Redis网络库源码分析(3)之ae.c

    一.aeCreateEventLoop & aeCreateFileEvent 上一篇文章中,我们已经将服务器启动,只是其中有些细节我们跳过了,比如aeCreateEventLoop函数到底做 ...

  3. Redis网络库源码分析(2)之启动服务器

    一.从main开始 main函数定义在server.c中,它的内容如下: //server.c int main() { signal(SIGPIPE, SIG_IGN); //忽略SIGPIPE信号 ...

  4. ApplicationEvent事件机制源码分析

    <spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情> <服务网关zu ...

  5. Zepto事件模块源码分析

    Zepto事件模块源码分析 一.保存事件数据的handlers 我们知道js原生api中要移除事件,需要传入绑定时的回调函数.而Zepto则可以不传入回调函数,直接移除对应类型的所有事件.原因就在于Z ...

  6. Redis网络模型的源码分析

    Redis的网络模型是基于I/O多路复用程序来实现的.源码中包含四种多路复用函数库epoll.select.evport.kqueue.在程序编译时会根据系统自动选择这四种库其中之一.下面以epoll ...

  7. Redis 内存管理 源码分析

    要想了解redis底层的内存管理是如何进行的,直接看源码绝对是一个很好的选择 下面是我添加了详细注释的源码,需要注意的是,为了便于源码分析,我把redis为了弥补平台差异的那部分代码删了,只需要知道有 ...

  8. springBoot集成Redis遇到的坑(择库)源码分析为什么择库失败

    提示: springboot提供了一套链接redis的api,也就是个jar包,用到的连接类叫做LettuceConnectionConfiguration,所以我们引入pom时是这样的 <de ...

  9. cJSON库源码分析

    本文采用以下协议进行授权: 自由转载-非衍生-保持署名|Creative Commons BY-NC-ND 3.0 ,转载请注明作者及出处. cJSON是一个超轻巧,携带方便,单文件,简单的可以作为A ...

随机推荐

  1. react-native navigation的学习与使用

    在很久之前,RN中文网说推荐用react-navigation替代navigator作为新的导航库,从RN 0.43版本开始,官方就已经停止维护Navigator了,所以强烈建议大家迁移到新的reac ...

  2. GRE/GMAT/LSAT长难句300例精讲精练-思维导图

    <GRE/GMAT/LSAT长难句300例精讲精练>是GRE超人气名师陈琦老师团队的又一本新作,也是“再要你命3000”的新成员,从之前的词汇.短语.练习,提升到长难句层面,相信学完本书后 ...

  3. Spring -- spring 和 hibernate 整合

    1.概述, 事务管理, 编程式和说明式事务管理 2. 事务属性 传播行为: 传播行为 意义 PROPAGATION_MANDATORY 该方法必须运行在一个事务中.如果当前事务不存在,将抛出一个异常. ...

  4. XML 存储文档

    package com.kpsh.myself; import java.io.File;import java.io.FileWriter; import org.dom4j.Document;im ...

  5. VUE 结合 vue-resource 进行ajax操作

    有意思的! 初始化需要ajax获取数据! 搜索商品需要ajax获取数据! 提交数据需要ajax传递数据! 有了 vue-resource ,操作挺方便的. 这是html <form class= ...

  6. No toolchains found in the NDK toolchains folder for ABI with prefix

    通过Android Studio 的Sdk Manager安装NDK,安装完之后编译失败,报错信息如下: No toolchains found in the NDK toolchains folde ...

  7. java8 日期处理

    这两周写业务逻辑,总会有各种日期操作,但是又记不住API,就是记不住API啊 这篇博客不错,记下来 https://lw900925.github.io/java/java8-newtime-api. ...

  8. Eclipse和MyEclipse的区别

    翻译:日食,月食. eclipse是免费的,myeclipse是收费的. myeclipse是eclipse的插件.

  9. Dib to Bitmap doesn't work in WPF

    一.Dib to Bitmap doesn't work in WPF 代码如下: protected byte[] BitmapFromDIB(IntPtr pDIB, IntPtr pPix) { ...

  10. C++(十八) — 内存模式、堆和栈

    1.内存模式 一个程序执行时,先复制到内存,然后CPU逐句读取指令执行. 每个存储单元存放一个字节(8bit)数据,每个有一个唯一的地址,地址是顺序编码的.比如:一台计算机256MB内存,则有256* ...