每个cs程序尤其是高并发的网络服务端程序都有自己的网络异步事件处理库,redis不例外。

事件库仅仅包括ae.c、ae.h,还有3个不同的多路复用(本文仅描述epoll)的wrapper文件,事件库封装了框架调用的主循环函数,暴露了时间、文件事件注册和销毁函数,典型的依赖反转模式。
网络操作都在networking.c里,封装了常见的socket操作。

我们从redis启动的main函数开始,从用户发出连接键入命令开始遍历网络事件库所涉及的函数,unix套接口相关函数不表。

首先对几个最常用对象进行解释。

//redis启动的时候(init_server())会创建的一个全局使用的事件循环结构
typedef struct aeEventLoop {
int maxfd; //仅仅是select 使用
long long timeEventNextId;
aeFileEvent events[AE_SETSIZE]; //用于保存epoll需要关注的文件事件的fd、触发条件、注册函数。
aeFiredEvent fired[AE_SETSIZE]; //epoll_wait之后获得可读或者可写的fd数组,通过aeFiredEvent->fd再定位到events。
aeTimeEvent *timeEventHead; //以链表形式保存多个时间事件,每隔一段时间机会触发注册的函数。
int stop;
void *apidata; //每种多路复用方法的使用的私有变量,例如epoll就是epfd和一个事件数组;而select是保存rset、wset。
aeBeforeSleepProc *beforesleep; // sleep之前调用的函数,有些事情是每次循环必须做的,并非文件、时间事件。
} aeEventLoop; //文件可读写事件
typedef struct aeFileEvent {
int mask; //触发条件:读、写
aeFileProc *rfileProc; //当fd可读时执行的事件(accept,read)
aeFileProc *wfileProc; //当fd可写时执行的事件(write)
void *clientData; //caller 传入的数据指针
} aeFileEvent; //超时时间事件
typedef struct aeTimeEvent {
long long id; /* time event identifier. */
long when_sec; /* seconds */
long when_ms; /* milliseconds */
aeTimeProc *timeProc; //当出现超时的时候所执行的事件
aeEventFinalizerProc *finalizerProc;
void *clientData; //caller传入的数据指针
struct aeTimeEvent *next; //单链表指向下一个时间事件
} aeTimeEvent; //从epoll_wait或者select返回的,已经触发的文件事件
typedef struct aeFiredEvent {
int fd;
int mask;
} aeFiredEvent;

我们来模拟redis server 启动和用户键入命令的过程,先上图。

//好吧,从main函数开始吧。

// src/redis.c 853
server.el = aeCreateEventLoop(); //创建一个aeEventLoop结构体,成员apidata指向一个aeApiState对象,如果使用epoll,fd是由epoll_create创建的全局epoll fd ,events[] 用于保存epoll_wait返回的事件组。 // src/redis.c 866
server.ipfd = anetTcpServer(server.neterr,server.port,server.bindaddr); //创建监听。 // src/redis.c 903
aeCreateTimeEvent(server.el, , serverCron, NULL, NULL); //注册一个时间事件serverCron,作用以后再讲。 // src/redis.c 906
aeCreateFileEvent(server.el,server.ipfd,AE_READABLE, acceptTcpHandler,NULL) //为监听fd的注册一个文件事件,首先把listenfd和触发条件epoll_ctl加入到全局的epoll fd进行监控。再以fd作为文件事件event数组index定位,mask填入只读,rfileProc填入acceptTcpHandler函数。 // src/redis.c 1571
aeSetBeforeSleepProc(server.el,beforeSleep); //aeEventLoop注册一个beforeSleep函数,这个函数在主循环里每次会被调用,作用以后再讲。 // src/ redis.c 1572
aeMain() //网络事件库的核心循环函数。 // src/ae.c 379
aeCreateLoop->beforesleep() //就是上面注册的beforeSleep函数。 // src/ae.c 275
aeProcessEvents(eventLoop, AE_ALL_EVENTS); //先调度aeApiPoll,用epoll_wait处理文件事件,返回的fd和触发条件先存储在eventLoop->fired[]里,然后根据fired[]每个事件的的fd,定位到events,根据触发条件调用已经注册的事件。 //文件事件处理完毕后,接下来就是处理超时时间事件,这里不表。 //假如有个用户连接上redis,则从redis servere就从上面的aeApiPoll的poll_wait返回,产生的只读事件会调度上面注册了acceptTcpHandler函数。 // src/networking.c 390
acceptTcpHandler(eventLoop, fd, NULL, AE_READABLE) //accept返回产生了一个新连接的fd(这里省略了很多步骤),需要从新的连接读取数据,于是为这个fd注册可读事件。 // src/networking.c 20
aeCreateFileEvent(server.el,fd,AE_READABLE, readQueryFromClient, c) //于是调用了aeCreateFileEvent 为只读事件注册, 这里的步骤和上面的listen fd 一样。用epoll_ctl将fd加入到epoll fd里等待下次epoll_wait,再注册触发条件和readQueyFromClient函数,接着aeMain()继续执行等待用户的数据。 //假如用户在这个连接键入redis命令例如:set foo bar,redis server端将会从aeApiPoll的epoll_wait返回,和accept一样的处理方式。返回的fd填入到fired[]数组,通过fired[fd]->fd找到是哪个文件事件,找到events[fd]->rfielProc这个函数,就是上面注册的readQueyFromClient 函数。 // src/networking.c 816
readQueyFromClient(server.el, fd, NULL, AE_READABLE) //这个函数首先会执行read从tcp buffer读取用户键入的命令(非阻塞io),然后处理buffer,找到对应的command(lookupCommand),接下来执行这个command(call(c,cmd)),命令执行完毕需要返回结果的时候,会再次注册一个文件事件 // src/networking.c 71
aeCreateFileEvent(server.el, c->fd, AE_WRITABLE, sendReplyToClient, c) //这样下次循环epoll_wait的时候就发现这个fd可写,于是就会执行//sendReplyToClient,讲结果发送给client。 //redis的这个网络事件库是比较标准的网络框架的模式,实现的功能不算多但够用。

原文:http://www.hoterran.info/redis_eventlibrary

(转)redis源代码分析 – event library的更多相关文章

  1. Redis源代码分析(一)--Redis结构解析

    从今天起,本人将会展开对Redis源代码的学习,Redis的代码规模比較小,很适合学习,是一份很不错的学习资料,数了一下大概100个文件左右的样子,用的是C语言写的.希望终于能把他啃完吧,C语言好久不 ...

  2. redis 源代码分析(一) 内存管理

    一,redis内存管理介绍 redis是一个基于内存的key-value的数据库,其内存管理是很重要的,为了屏蔽不同平台之间的差异,以及统计内存占用量等,redis对内存分配函数进行了一层封装,程序中 ...

  3. Redis源代码分析(23)--- CRC循环冗余算法RAND随机数的算法

    他今天就开始学习Redis源代码的一些工具来实现,在任何一种语言工具.算法实现的原理应该是相同的,一些比較经典的算法.比方说我今天看的Crc循环冗余校验算法和rand随机数产生算法. CRC算法全称循 ...

  4. Redis源代码分析(十一年)--- memtest内存测试

    今天,我们继续redis源代码test下测试在封装中的其它文件.今天读数memtest档,翻译了,那是,memory test 存储器测试工具..可是里面的提及了非常多东西,也给我涨了非常多见识,网上 ...

  5. Redis源代码分析(三)---dict哈希结构

    昨天分析完adlist的Redis代码.今天立即马不停蹄的继续学习Redis代码中的哈希部分的结构学习,只是在这里他不叫什么hashMap,而是叫dict.并且是一种全新设计的一种哈希结构,他仅仅是通 ...

  6. Redis源代码分析(六)--- ziplist压缩列表

    ziplist和之前我解析过的adlist列表名字看上去的非常像.可是作用却全然不同.之前的adlist主要针对的是普通的数据链表操作. 而今天的ziplist指的是压缩链表.为什么叫压缩链表呢.由于 ...

  7. Redis源代码分析(二十)--- ae事件驱动

    事件驱动的术语出现更频繁.听起来非常大的,今天我把Redis内部驱动器模型来研究它,奖励的感觉啊.一个ae.c主程序,加4文件的事件类型,让你彻底弄清楚,Redis是怎样处理这些事件的. 在Redis ...

  8. Redis源代码分析(五)--- sparkline微线图

    sparkline这个单词,我第一次看的时候.也不知道这什么意思啊,曾经根本没听过啊,可是这真真实实的出如今了redis的代码中了,刚刚開始以为这也是属于普通的队列嘛.就把他分在了struct包里了. ...

  9. Redis源代码分析(十三)--- redis-benchmark性能測试

    今天讲的这个是用来给redis数据库做性能測试的,说到性能測试,感觉这必定是高大上的操作了.redis性能測试.測的究竟是哪方面的性能,怎样測试,通过什么指标反映此次測试的性能好坏呢.以下我通过源代码 ...

随机推荐

  1. 洛谷 U3346 A1-偶回文数

    U3346 A1-偶回文数 题目背景 方方方很喜欢回文数,于是zzq就出了一道关于回文数的题目. 因为偶回文数比较简单,所以方方方就把它放在了第一题... 题目描述 我们定义一个长度为偶数的回文数叫做 ...

  2. NHibernate之旅(18):初探代码生成工具使用

    本节内容 引入 代码生成工具 结语 引入 我们花了大量的篇幅介绍了相关NHibernate的知识.一直都是带着大家手动编写代码,首先创建数据库架构.然后编写持久化类和映射文件,最后编写数据操作方法.測 ...

  3. mbed

    mbed介绍--ARM最新面向IOT的RTOS与平台 文章为本人原创,转载请注明:http://blog.csdn.net/guo8113/article/details/40479303 mbed是 ...

  4. How to improve Java's I/O performance( 提升 java i/o 性能)

    原文:http://www.javaworld.com/article/2077523/build-ci-sdlc/java-tip-26--how-to-improve-java-s-i-o-per ...

  5. c++面向对象程序设计 谭浩强 第一章答案

    c++面向对象程序设计 谭浩强 答案 第一章 目录: c++面向对象程序设计 谭浩强 答案 第一章 c++面向对象程序设计 谭浩强 答案 第二章 c++面向对象程序设计 谭浩强 答案 第三章 c++面 ...

  6. SQL语句之WITH AS

    一.WITH AS的含义 WITH AS短语,也叫做子查询部分(subquery factoring),可以让你做很多事情,定义一个SQL片断,该SQL片断会被整个SQL语句所用到. 其实就是把一大堆 ...

  7. c# TextBox

    1. text内容全选事件 textBox1.selectAll(); 2.失去与获取焦点事件 textox1.LostFocus += new EventHandler(txt_LostFocus) ...

  8. c#可自定义码表的base64加密解密算法类

    000 using System; using System.Collections.Generic; using System.Linq; using System.Text; using Syst ...

  9. Projective Texture Mapping - 投影纹理

    昨天导师让写一个投影纹理,将一个相机渲染的图片的一部分投影到另外一个相机里面,目的是无缝的拼接. 投影纹理就和shadow map一样,都是将片元转换到另外一个相机/光源坐标系下,投影后找到对应的纹素 ...

  10. Javescript 面向对象编程 — 封装

    生成实例对象的原始模式 <script type="text/javascript"> var Cat={ name:'波斯猫', color:'White' } al ...