Redis 的高可用 很大程度上要归功于其 为 事件驱动服务工具,采用
Reactor 模式使得其 拥有高性能 IO 操作
首先我们先科普一下什么叫 Reactor 模式,即 事件驱动机制,应用程序提供相应的接口并注册到 Reactor 上,如果相应的事件发生,Reactor 将主动调用之前注册的接口,而这些接口又称为 回调函数
我们知道最最原始的网络编程的思想就是一个 while 循环,比如针对 端口连接套接字,那么就是 循环监听 端口套接字是否有连接事件发生,如果有的话就调用事件处理接口,这种方式最大的问题就是没有并发能力,因为 accept 是堵塞的,而且 如果 事件处理接口一直没有处理完毕,后面端口监听事件也就只能一直被阻塞
后来,有大牛就想到了使用多线程进行并发事件监控,Tomcat 早期的版本就是使用这个思路进行 并发连接的处理,这样一来确实能解决一定量的并发业务,提高了服务器的吞吐量,每个线程里面的 accept 阻塞不能影响其他连接的处理,但是缺点也比较明显,每次 线程的 新建-销毁 确实很耗费资源,并发数受系统资源的限制比较厉害;而且我们可以注意到 每次整个连接业务都是整体由一个线程负责,如果把 业务分开(接受连接、业务处理)呢?
所以就有了现在普遍使用的方式:IO 多路复用模型(IO 模型方面感兴趣的童鞋可以去 Google 一下,或者等我后面 IO 模型专题 文章 ^v^,手动挖坑~),将阻塞的 accept 部分业务放到 一个主线程里面 监听,如果有 连接过来,回调事件接口,分发给 子线程进行业务处理,也就是 线程池的概念;当然这里的 线程池实现方式 在不同软件中也可能是由 进程池 取代;不同的系统提供了不同的 IO多路复用 实现方式(select、poll、epoll 等),之前我们讲解的 渐进式解析 memcached 源码 - 线程机制 这篇有提到,不过 memcached 使用的是 libevent 库,libevent 将不同系统的实现方式进行了封装,被应用程序调用的时候会根据系统选择更优的实现方式;
Redis 的文件事件 在实现 Reactor 的时候,采用了自己封装 各个系统中的 IO 多路复用 的方式,而不是像 memcached 一样使用第三方库
Redis 事件
Redis 中事件主要有两种类型:
文件事件 ( file event )Redis 文件事件一般都是 套接字的 读写状态监控,然后回调相应接口进行处理时间事件 ( time event )时间事件 则是维护一个定时器,每当满足预设的时间要求,就将该时间事件标记为待处理,然后在 Redis 的事件循环中进行处理。Redis 对于这两种事件的处理优先级是 文件事件优先于时间事件
文件事件
文件事件的结构体为
就绪的文件事件结构体为
我们上面提到过 Redis 封装了 select、epoll、evport、kqueue 多个 IO 多路复用实现方式,根据不同系统选择不同的实现方式,这个判断是由 宏定义决定
我们也可以通过 info server 命令的 multiplexing_api 属性查看具体的 多路复用选择
时间事件
时间事件的结构体为
时间事件为一个双向链表
事件状态
事件池状态结构体为
比如我们常用的 epoll 库,*apidata 存储的数据结构如下
源码解析
由于有些方法里的代码量比较大,我们这里按照 典型的代码片段进行解析,同志们可以根据文章提示的代码位置 和 代码里面的关键词 在源码中搜素,可能数据结构一些元素 看不太懂什么意思,没关系,先混个脸熟,后面看完回头再看过来就明白了
向 Redis 事件表 aeEventLoop 的 event 注册一个事件,epoll 对应的是 epoll_ctl 函数,ae 事件库函数为 aeApiAddEvent()
函数 aeApiPoll() 调用底层 IO 复用函数 epoll_wait 来获取准备好的事件描述符
就绪事件的循环遍历处理我们之前已经分析过了,不知道大家是否还记得,详见 REDIS 服务器 源码分析中的 事件处理器部分,也可以搜索 aeProcessEvents() 函数
就绪事件循环中最终还是为了 回调函数的处理,比如 acceptTcpHandler()
而时间事件的添加函数为 aeCreateTimeEvent(),逻辑比较简单,主要是将事件的 时间等属性加入 aeEventLoop 事件表中,比如我们常见的 serverCron 循环事件;由 processTimeEvents() 函数进行循环处理,类似上面的 aeProcessEvents() 函数的逻辑
以上为比较经典的函数逻辑解析,另外我从 其他资料找到了 一些 API 的详解,大家可以将就看看
本文作者: wettper
本文链接: http://www.web-lovers.com/redis-source-ae.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!