这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战
reactor 模型,本质是处理高并发IO的一种请求编程模型。
主要是由3个模块组成:
- reactor ⇒ 整体调度
- acceptor ⇒ 接受事件
- handler ⇒ 处理事件
而对 IO 世界来说,整体也有 3 种事件:
- 连接事件
- 写事件
- 读事件
那么事件如何贯穿在整个模型中呢?再说玄学一点就是:数据是怎么随着事件在整个模型里面传播的?
-
连接事件 → acceptor 接受连接 → 创建 handler,用于之后连接的读写
-
读写事件 → handler 中处理
-
整个引擎 → reactor
- 连接事件,转发给 acceptor
- 读写事件,转发给 handler
- 没有事件,loop 循环等待事件到来
所以,这三个模块是围绕事件的监听、转发和处理来进行交互的。
事件初始化
什么叫事件初始化?
首先整个调度需要知道监听哪些事件,事件对应的处理器函数又是什么?
初始化做的也就是这两件事:
- 创建需要监听的事件类型
- 注册对应的事件处理函数
事件初始化完成后,服务器程序就需要进入到 事件捕获、分发和处理的主循环中。
而整个模型的基本工作就都在这 3个 操作里面:
客户端的不同类请求会在服务器端触发连接、读、写 3 类事件,这 3 类事件的监听、分发和处理又是由 reactor、acceptor、handler 3 个模块完成,然后这 3 个模块会通过事件驱动框架来实现交互和事件处理reactor 模型
支撑整个框架运行的主要函数:
- 框架主循环的 aeMain()
- 实践捕获和分发的 aeProcessEvents()
- 负责事件和 handler注册的 aeCreateFileEvent()
主循环:
就是不断判断事件循环的结束标志:如果被标记为 true,整个就结束了
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
// 判断是否结束
while (!eventLoop->stop) {
…
aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
}
}
事件捕获和分发:
触发的分支有如下
- 没有时间时间,也没有网络IO事件 → 直接返回,回到主循环,开始下一轮循环
- 有IO事件/紧急处理的时间事件 → 捕获发生的IO事件,进行处理
- 普通的时间事件 → 调用时间事件处理函数: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多路复用机制。所以会封装不同操作系统的实现