文档章节

redis源代码分析 – event library

Dicky
 Dicky
发布于 2011/09/22 20:03
字数 1924
阅读 624
收藏 1

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

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

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

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

redis_ae

//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 启动和用户键入命令的过程,先上图。

redis_network_event_arch

好吧,从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, 1, 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的这个网络事件库是比较标准的网络框架的模式,实现的功能不算多但够用。

3 Comments

  1. [...] redis的网络事件库,我们在前面的文章已经讲过,readQueryFromClient先从fd中读取数据,先存储在c->querybuf里(networking.c 823)。接下来函数processInputBuffer来解析querybuf,上面说过如果是telnet发送的裸协议数据是没有*打头的表示参数个数的辅助信息,针对telnet的数据跳到processInlineBuffer函数,而其他则通过函数processMultibulkBuffer。 这两个函数的作用一样,解析c->querybuf的字符串,分解成多参数到c->argc和c->argv里面,argc表示参数的个数,argv是个redis_object的指针数组,每个指针指向一个redis_object, object的ptr里存储具体的内容,对于”get a“的请求转化后,argc就是2,argv就是 [...]

  2. [...] redis的网络事件库,我们在前面的文章已经讲过,readQueryFromClient先从fd中读取数据,先存储在c->querybuf里(networking.c 823)。接下来函数processInputBuffer来解析querybuf,上面说过如果是telnet发送的裸协议数据是没有*打头的表示参数个数的辅助信息,针对telnet的数据跳到processInlineBuffer函数,而其他则通过函数processMultibulkBuffer。 这两个函数的作用一样,解析c->querybuf的字符串,分解成多参数到c->argc和c->argv里面,argc表示参数的个数,argv是个redis_object的指针数组,每个指针指向一个redis_object, object的ptr里存储具体的内容,对于”get a“的请求转化后,argc就是2,argv就是 [...]

  3. [...] redis的网络事件库,我们在前面的文章已 经讲过,readQueryFromClient先从fd中读取数据,先存储在c->querybuf里(networking.c 823)。接下来函数processInputBuffer来解析querybuf,上面说过如果是telnet发送的裸协议数据是没有*打头的表示参数 个数的辅助信息,针对telnet的数据跳到processInlineBuffer函数,而其他则通过函数 processMultibulkBuffer。 这两个函数的作用一样,解析c->querybuf的字符串,分解成多参数到c->argc和c->argv里面,argc表示参数的 个数,argv是个redis_object的指针数组,每个指针指向一个redis_object, object的ptr里存储具体的内容,对于”get a“的请求转化后,argc就是2,argv就是 [...]

本文转载自:http://www.hoterran.info/redis_eventlibrary

共有 人打赏支持
Dicky
粉丝 13
博文 92
码字总数 19950
作品 0
成都
后端工程师
redis 源代码分析 – event library

每个cs程序尤其是高并发的网络服务端程序都有自己的网络异步事件处理库,redis不例外。 事件库仅仅包括ae.c、ae.h,还有3个不同的多路复用(本文仅描述epoll)的wrapper文件,事件库封装了框...

鉴客
2011/08/31
526
0
ubuntu下安装sourcenav过程

ubuntu10.04默认安装gcc编译器是4.4版本 sourcenav-6.0.tar.gz是Linux下的源代码查看器的源代码文件。通过下载sourcenav-6.0.tar.gz来进行安装。 下载地址: http://nchc.dl.sourceforge.net...

mfcai
2012/09/17
0
0
(六)编译安装zabbix3.4.4执行make报致命错误:event.h没有这个文件解决+分析方法

编译安装zabbix3.4.4执行make报致命错误:event.h没有这个文件解决+分析方法 如有错误直接指正,多谢! 1、编译过程 #./configure--prefix=/usr/local/zabbix-2.2.2/ --enable-server --enab...

火头一号
2017/11/15
0
0
授之以渔-运维平台发布模块二(Jenkins篇)

接上篇《授之以渔-运维平台发布模块一(Jenkins篇)》,今天介绍下结合着Saltstack的MasterEven,用来做发布系统的结果监控。 一、Event介绍 SaltStack 0.10版本中, 新增了Event系统, 官方在...

大Q的梦想
2017/06/10
0
0
Redis3.0 Cluster集群安装

部署图 同一物理机部署6个Redis进程,7001、7002、7003端口为Master进程、7004、7005、7006端口为Slave进程。多机部署时注意配对的Master和Slave不在同一物理机上即可。 安装Redis 下载源代码...

Elisabth
2015/04/16
0
3

没有更多内容

加载失败,请刷新页面

加载更多

下一页

java并发备忘

不安全的“先检查后执行”,代码形式如下: if(条件满足){ //这里容易出现线程安全问题//doSomething}else{//doOther} 读取-修改-写入 原子操作:使用CAS技术,即首先从V中读取...

Funcy1122
今天
0
0
SpringBoot2.0 停机

最近新建了个SpringBoot2.0的项目,因为原来一直使用的是传统的Tomcat部署war包的形式,所以这次SpringBoot内置Tomcat部署jar包的时候遇到了很多问题。其中一个就是因为没有外置的Tomcat容器...

Canaan_
昨天
0
1
Confluence 6 外部参考

一个外部参考的意思是任何站点链接到你 Confluence 的实例。任何时候当 Confluence 的用户单击这个外部链接的时候,Confluence 可以记录这次单击为参考。 在默认的情况下,外部链接的参考链接...

honeymose
昨天
0
0
Android中的设计模式之抽象工厂模式

参考 《设计模式解析》 第十一章 Abstract Factory模式 《设计模式:可复用面向对象软件的基础 》3.1 Abstract Factory 抽象工厂 对象创建型模式 《Android源码设计模式解析与实战》第6章 创...

newtrek
昨天
0
0
Redis | 地理空间(GEO)的一个坑

Redis的地理空间(Geo)是个好东西,轻轻松松的就可以把地图描点的问题处理了, 最近却遇到一个坑...Redis采用的Msater-Slave模式, 运用GEORADIUS在salve读取对应的数据,新增了从节点但是从不返...

云迹
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部