(转)redis源代码分析 – event library
每个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的更多相关文章
- Redis源代码分析(一)--Redis结构解析
从今天起,本人将会展开对Redis源代码的学习,Redis的代码规模比較小,很适合学习,是一份很不错的学习资料,数了一下大概100个文件左右的样子,用的是C语言写的.希望终于能把他啃完吧,C语言好久不 ...
- redis 源代码分析(一) 内存管理
一,redis内存管理介绍 redis是一个基于内存的key-value的数据库,其内存管理是很重要的,为了屏蔽不同平台之间的差异,以及统计内存占用量等,redis对内存分配函数进行了一层封装,程序中 ...
- Redis源代码分析(23)--- CRC循环冗余算法RAND随机数的算法
他今天就开始学习Redis源代码的一些工具来实现,在任何一种语言工具.算法实现的原理应该是相同的,一些比較经典的算法.比方说我今天看的Crc循环冗余校验算法和rand随机数产生算法. CRC算法全称循 ...
- Redis源代码分析(十一年)--- memtest内存测试
今天,我们继续redis源代码test下测试在封装中的其它文件.今天读数memtest档,翻译了,那是,memory test 存储器测试工具..可是里面的提及了非常多东西,也给我涨了非常多见识,网上 ...
- Redis源代码分析(三)---dict哈希结构
昨天分析完adlist的Redis代码.今天立即马不停蹄的继续学习Redis代码中的哈希部分的结构学习,只是在这里他不叫什么hashMap,而是叫dict.并且是一种全新设计的一种哈希结构,他仅仅是通 ...
- Redis源代码分析(六)--- ziplist压缩列表
ziplist和之前我解析过的adlist列表名字看上去的非常像.可是作用却全然不同.之前的adlist主要针对的是普通的数据链表操作. 而今天的ziplist指的是压缩链表.为什么叫压缩链表呢.由于 ...
- Redis源代码分析(二十)--- ae事件驱动
事件驱动的术语出现更频繁.听起来非常大的,今天我把Redis内部驱动器模型来研究它,奖励的感觉啊.一个ae.c主程序,加4文件的事件类型,让你彻底弄清楚,Redis是怎样处理这些事件的. 在Redis ...
- Redis源代码分析(五)--- sparkline微线图
sparkline这个单词,我第一次看的时候.也不知道这什么意思啊,曾经根本没听过啊,可是这真真实实的出如今了redis的代码中了,刚刚開始以为这也是属于普通的队列嘛.就把他分在了struct包里了. ...
- Redis源代码分析(十三)--- redis-benchmark性能測试
今天讲的这个是用来给redis数据库做性能測试的,说到性能測试,感觉这必定是高大上的操作了.redis性能測试.測的究竟是哪方面的性能,怎样測试,通过什么指标反映此次測试的性能好坏呢.以下我通过源代码 ...
随机推荐
- HDU5979 Convex
/* HDU5979 Convex http://acm.hdu.edu.cn/showproblem.php?pid=5979 计算几何 三角形面积公式 * * */ #include <cs ...
- _DataStructure_C_Impl:AOE网的关键路径
//_DataStructure_C_Impl:CriticalPath #include<stdio.h> #include<stdlib.h> #include<st ...
- BZOJ 2435: [Noi2011]道路修建 dfs搜图
2435: [Noi2011]道路修建 Description 在 W 星球上有 n 个国家.为了各自国家的经济发展,他们决定在各个国家之间建设双向道路使得国家之间连通.但是每个国家的国王都很吝啬,他 ...
- 29.QT主窗口加widget
运行效果 widget布局showwidget.h #ifndef SHOWWIDGET_H #define SHOWWIDGET_H #include <QWidget> #includ ...
- BZOJ 3238 后缀数组+单调栈
单调栈跑两遍求出来 ht[i]为最小值的那段区间 //By SiriusRen #include <cstdio> #include <cstring> #include &l ...
- 抓取git的log文件批处理命令示例
@echoset sincedate="2016-04-28 00:00:01" ::变量set beforedate="2016-04-29 00:0 ...
- HD-ACM算法专攻系列(17)——find your present (2)
题目描述: 源码: #include"iostream" #include"string" using namespace std; bool IsFirstH ...
- 自定义TempData跨平台思路
一:TempData的自定义实现... TempData是用Session实现的,既然是Session,那模式是线程方式...这样的Session是没法进行跨平台的... 那么这就涉及到如何在多台机器 ...
- .NET MVC权限设计思考之切入点
在WebForm下我们一般会设计个PageBase继承Page,在OnInit方法中实现对基本权限的验证业务,然后所有的页面在继承PageBase直接继承这项基本权验证业务.而在.NET MVC下我们 ...
- onTouchEvent事件
@Override public boolean onTouchEvent(MotionEvent event) { if(event.getAction() == MotionEvent.ACTIO ...