redis源码解析之事件驱动

Redis 内部有个小型的事件驱动,它主要处理两项任务:

  1. 文件事件:使用I/O多路复用技术处理多个客户端请求,并返回执行结果。
  2. 时间事件:维护服务器的资源管理,状态检查。

主要的数据结构包括文件事件结构体,时间事件结构体,触发事件结构体,事件循环结构体

/* File event structure */
typedef struct aeFileEvent {int mask; /* one of AE_(READABLE|WRITABLE) */aeFileProc *rfileProc;aeFileProc *wfileProc;void *clientData;
} aeFileEvent;/* Time event structure */
typedef struct aeTimeEvent {long long id; /* time event identifier. */long when_sec; /* seconds */long when_ms; /* milliseconds */aeTimeProc *timeProc;aeEventFinalizerProc *finalizerProc;void *clientData;struct aeTimeEvent *next;
} aeTimeEvent;/* A fired event */
typedef struct aeFiredEvent {int fd;int mask;
} aeFiredEvent;/* State of an event based program */
typedef struct aeEventLoop {int maxfd;   /* highest file descriptor currently registered */int setsize; /* max number of file descriptors tracked */long long timeEventNextId;time_t lastTime;     /* Used to detect system clock skew */aeFileEvent *events; /* Registered events */aeFiredEvent *fired; /* Fired events */aeTimeEvent *timeEventHead;int stop;void *apidata; /* This is used for polling API specific data */aeBeforeSleepProc *beforesleep;
} aeEventLoop;

首先通过aeCreateEventLoop()函数创建时间循环结构体

aeEventLoop *aeCreateEventLoop(int setsize) {aeEventLoop *eventLoop;int i;if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;eventLoop->setsize = setsize;eventLoop->lastTime = time(NULL);eventLoop->timeEventHead = NULL;eventLoop->timeEventNextId = 0;eventLoop->stop = 0;eventLoop->maxfd = -1;eventLoop->beforesleep = NULL;if (aeApiCreate(eventLoop) == -1) goto err;/* Events with mask == AE_NONE are not set. So let's initialize the* vector with it. */for (i = 0; i < setsize; i++)eventLoop->events[i].mask = AE_NONE;return eventLoop;err:if (eventLoop) {zfree(eventLoop->events);zfree(eventLoop->fired);zfree(eventLoop);}return NULL;
}

其中aeApiCreate()是核心处理函数,redis 根据不同系统构建了不同的多路复用实现:例如linux的epoll,OS X的kqueue,windows的select。

initServer() 为监听套接字注册了读事件acceptTcpHandler()或者 acceptUnixHandler()。起作用是当有客户端连接进来的时候调用它,并注册读时间,回调函数为从客户端读取命令的函数readQueryFromClient()。

创建了aeEventLoop后就进入时间循环aeProcessEvents()中,调用aeApiPoll()来监听事件发生。

获取阻塞时间后,就开始文件事件的触发获取。得到所有触发事件,然后遍历文件事件触发数组(eventLoop->fired),得到fd,然后获取对应的文件事件,这里的fired已经看出,它只是个索引。调用对应的回调函数,结束文件事件的处理。

int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{int processed = 0, numevents;/* Nothing to do? return ASAP */if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;/* Note that we want call select() even if there are no* file events to process as long as we want to process time* events, in order to sleep until the next time event is ready* to fire. */if (eventLoop->maxfd != -1 ||((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {int j;aeTimeEvent *shortest = NULL;struct timeval tv, *tvp;if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))shortest = aeSearchNearestTimer(eventLoop);if (shortest) {long now_sec, now_ms;/* Calculate the time missing for the nearest* timer to fire. */aeGetTime(&now_sec, &now_ms);tvp = &tv;tvp->tv_sec = shortest->when_sec - now_sec;if (shortest->when_ms < now_ms) {tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000;tvp->tv_sec --;} else {tvp->tv_usec = (shortest->when_ms - now_ms)*1000;}if (tvp->tv_sec < 0) tvp->tv_sec = 0;if (tvp->tv_usec < 0) tvp->tv_usec = 0;} else {/* If we have to check for events but need to return* ASAP because of AE_DONT_WAIT we need to set the timeout* to zero */if (flags & AE_DONT_WAIT) {tv.tv_sec = tv.tv_usec = 0;tvp = &tv;} else {/* Otherwise we can block */tvp = NULL; /* wait forever */}}numevents = aeApiPoll(eventLoop, tvp);for (j = 0; j < numevents; j++) {aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];int mask = eventLoop->fired[j].mask;int fd = eventLoop->fired[j].fd;int rfired = 0;/* note the fe->mask & mask & ... code: maybe an already processed* event removed an element that fired and we still didn't* processed, so we check if the event is still valid. */if (fe->mask & mask & AE_READABLE) {rfired = 1;fe->rfileProc(eventLoop,fd,fe->clientData,mask);}if (fe->mask & mask & AE_WRITABLE) {if (!rfired || fe->wfileProc != fe->rfileProc)fe->wfileProc(eventLoop,fd,fe->clientData,mask);}processed++;}}/* Check time events */if (flags & AE_TIME_EVENTS)processed += processTimeEvents(eventLoop);return processed; /* return the number of processed file/time events */
}

当用户在客户端输入命令后,触发读时间,服务器调用readQueryFromClient()来读取命令,每条命令处理完成之后都会调用addReply(),其中之一的作用是注册写事件,回调函数sendReplyToClient(),目的是将处理结果写回客户端。

处理完文件事件后再执行时间事件(serverCron)

TimeEvent被组织为一个单向链表,表头指针timeEventHead保存在核心数据结构aeEventLoop中。aeMain函数在每一轮循环中都会遍历该链表,针对每个TimeEvent,先调用gettimeofday获取系统当前时间,如果它比TimeEvent中的时间要小,则说明TimeEvent还没触发,应继续前进,否则说明TimeEvent已经触发了,立即调用超时处理函数,接下来根据处理函数的返回值分两种情况讨论:

1)若处理函数返回-1,那么把这个TimeEvent删掉。

2)否则,根据返回值修改当前的TimeEvent。比如返回5000,这个TimeEvent就会在5秒后再次被触发。

由于情况1)我们不能由当前结点到达下一结点,于是就又从表头开始遍历。

在目前的版本中,正常模式下的Redis 只带有serverCron 一个时间事件,而在benchmark 模
式下,Redis 也只使用两个时间事件。
在这种情况下,程序几乎是将无序链表退化成一个指针来使用,所以使用无序链表来保存时间
事件,并不影响事件处理器的性能。

posted on 2014-06-28 23:52 Stinky tofu 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/loujiayu/p/3813964.html

redis源码解析之事件驱动相关推荐

  1. Redis源码解析——双向链表

    相对于之前介绍的字典和SDS字符串库,Redis的双向链表库则是非常标准的.教科书般简单的库.但是作为Redis源码的一部分,我决定还是要讲一讲的.(转载请指明出于breaksoftware的csdn ...

  2. Redis源码解析——字典基本操作

    有了<Redis源码解析--字典结构>的基础,我们便可以对dict的实现进行展开分析.(转载请指明出于breaksoftware的csdn博客) 创建字典 一般字典创建时,都是没有数据的, ...

  3. Redis源码解析——内存管理

    在<Redis源码解析--源码工程结构>一文中,我们介绍了Redis可能会根据环境或用户指定选择不同的内存管理库.在linux系统中,Redis默认使用jemalloc库.当然用户可以指定 ...

  4. Redis源码解析——前言

    今天开启Redis源码的阅读之旅.对于一些没有接触过开源代码分析的同学来说,可能这是一件很麻烦的事.但是我总觉得做一件事,不管有多大多难,我们首先要在战略上蔑视它,但是要在战术上重视它.除了一些高大上 ...

  5. Redis源码解析(15) 哨兵机制[2] 信息同步与TILT模式

    Redis源码解析(1) 动态字符串与链表 Redis源码解析(2) 字典与迭代器 Redis源码解析(3) 跳跃表 Redis源码解析(4) 整数集合 Redis源码解析(5) 压缩列表 Redis ...

  6. Redis源码解析——有序整数集

    有序整数集是Redis源码中一个以大尾(big endian)形式存储,由小到大排列且无重复的整型集合.它存储的类型包括16位.32位和64位的整型数.在介绍这个库的实现前,我们还需要先熟悉下大小尾内 ...

  7. Redis源码解析——Zipmap

    本文介绍的是Redis中Zipmap的原理和实现.(转载请指明出于breaksoftware的csdn博客) 基础结构 Zipmap是为了实现保存Pair(String,String)数据的结构,该结 ...

  8. Redis源码解析——字典结构

    C++语言中有标准的字典库,我们可以通过pair(key,value)的形式存储数据.但是C语言中没有这种的库,于是就需要自己实现.本文讲解的就是Redis源码中的字典库的实现方法.(转载请指明出于b ...

  9. Redis源码解析——字典遍历

    之前两篇博文讲解了字典库的基础,本文将讲解其遍历操作.之所以将遍历操作独立成一文来讲,是因为其中的内容和之前的基本操作还是有区别的.特别是高级遍历一节介绍的内容,充满了精妙设计的算法智慧.(转载请指明 ...

  10. Redis源码解析(1)——源码目录介绍

    概念 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合)和zset(有序集合).这些 ...

最新文章

  1. 【并发编程】线程池--Executor框架
  2. 【C语言入门教程】2.2 常量 与 变量
  3. 不可将您的方法命名为“等于”
  4. 数据库原理及应用-期末考试试题及答案
  5. 归并排序算法(java实现)
  6. VMware 安装 win7、win10、MAC 和网络模式VMnet0、VMnet1、VMnet8解释
  7. PDF reference 1.0-1.7 下载(免费)以及PDF结构介绍
  8. Cisco路由器的命令配置教程详解
  9. 韩昊 20190919-1 每周例行报告
  10. MOS管自举电路工作原理以及升压自举电路结构图分析
  11. 双系统、多系统快速切换
  12. 一个年轻程序员的成长,提升时间利用效率,第十四周总结
  13. Mac无法开机?别着急看这里
  14. 海康威视人脸识别智能终端获取设备事件二次开发Java
  15. 让PcShare2005vip免费
  16. windows10怎么备份操作系统
  17. Python基础篇4:判断用户输入的数是正数还是负数
  18. HTTP-web-Internet
  19. 量化交易 米筐 财务数据与定时器
  20. microsoft注册服务器32位,win7系统弹出microsoft注册服务器已停止工作的解决方法

热门文章

  1. bzoj 1047 理想的正方形
  2. LeetCode10 Regular Expression Matching
  3. [转]JavaScript字符串函数大全
  4. UITableView方法详解
  5. 地径 5~6公分 全冠红豆杉 100株待售
  6. Lambda表达式基础
  7. 使用VSCode开发Electron的初步入门
  8. GDB使用gdb-stl-views打印STL容器元素
  9. Python print()重定向 不输出到屏幕
  10. 一个基于protobuf的极简RPC