redis server「reactor 模型」

330 阅读2分钟

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

reactor 模型,本质是处理高并发IO的一种请求编程模型。

主要是由3个模块组成:

  1. reactor ⇒ 整体调度
  2. acceptor ⇒ 接受事件
  3. handler ⇒ 处理事件

而对 IO 世界来说,整体也有 3 种事件:

  • 连接事件
  • 写事件
  • 读事件

那么事件如何贯穿在整个模型中呢?再说玄学一点就是:数据是怎么随着事件在整个模型里面传播的?

  1. 连接事件 → acceptor 接受连接 → 创建 handler,用于之后连接的读写

  2. 读写事件 → handler 中处理

  3. 整个引擎 → reactor

    1. 连接事件,转发给 acceptor
    2. 读写事件,转发给 handler
    3. 没有事件,loop 循环等待事件到来

所以,这三个模块是围绕事件的监听、转发和处理来进行交互的。

事件初始化

什么叫事件初始化?

首先整个调度需要知道监听哪些事件,事件对应的处理器函数又是什么?

初始化做的也就是这两件事:

  1. 创建需要监听的事件类型
  2. 注册对应的事件处理函数

事件初始化完成后,服务器程序就需要进入到 事件捕获、分发和处理的主循环中。

而整个模型的基本工作就都在这 3个 操作里面:

客户端的不同类请求会在服务器端触发连接、读、写 3 类事件,这 3 类事件的监听、分发和处理又是由 reactor、acceptor、handler 3 个模块完成,然后这 3 个模块会通过事件驱动框架来实现交互和事件处理reactor 模型

支撑整个框架运行的主要函数:

  1. 框架主循环的 aeMain()
  2. 实践捕获和分发的 aeProcessEvents()
  3. 负责事件和 handler注册的 aeCreateFileEvent()

主循环:

就是不断判断事件循环的结束标志:如果被标记为 true,整个就结束了

void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;
    // 判断是否结束
    while (!eventLoop->stop) {
        …
        aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
    }
}

事件捕获和分发:

触发的分支有如下

  1. 没有时间时间,也没有网络IO事件 → 直接返回,回到主循环,开始下一轮循环
  2. 有IO事件/紧急处理的时间事件 → 捕获发生的IO事件,进行处理
  3. 普通的时间事件 → 调用时间事件处理函数:processTimeEvents()

着重看看第2种情况:

int aeProcessEvents(aeEventLoop *eventLoop, int flags){
   ...
   if (eventLoop->maxfd != -1 || (
				(flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
       ...
       // 调用aeApiPoll函数捕获事件
       numevents = aeApiPoll(eventLoop, tvp);
       ...
    }
    ...
}

实际上:aeApiPoll 依赖于OS提供的IO多路复用机制。所以会封装不同操作系统的实现