Redis 的网络框架是实现了 Reactor 模型吗?

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis 的网络框架是实现了 Reactor 模型吗?


Redis 一直被称为单线程架构,按照我们通常的理解,单个线程只能处理单个客户端的请求,但是在实际使用时,我们会看到 Redis 能同时和成百上千个客户端进行交互,这就是因为 Redis 基于 Reactor 模型,实现了高性能的网络框架,通过事件驱动框架,Redis 可以使用一个循环来不断捕获、分发和处理客户端产生的网络连接、数据读写事件。

Redis 的网络框架是实现了 Reactor 模型吗?

分成两部分来回答:

一是介绍 Reactor 模型是什么,二是说明 Redis 代码实现是如何与 Reactor 模型相对应的

什么是 Reactor 模型

Reactor 模型就是网络服务器端用来处理高并发网络 IO 请求的一种编程模型

我把这个模型的特征用两个“三”来总结,也就是:

  • 三类处理事件,即连接事件、写事件、读事件;
  • 三个关键角色,即 reactor、acceptor、handler。

Reactor 模型是如何基于这三类事件和三个角色来处理高并发请求的呢?

三类事件和 Reactor 模型的关系

Reactor 模型处理的是客户端和服务器端的交互过程,而这三类事件正好对应了客户端和服务器端交互过程中,不同类请求在服务器端引发的待处理事件

  • 当一个客户端要和服务器端进行交互时,客户端会向服务器端发送连接请求,以建立连接,这就对应了服务器端的一个连接事件
  • 一旦连接建立后,客户端会给服务器端发送读请求,以便读取数据。服务器端在处理读请求时,需要向客户端写回数据,这对应了服务器端的写事件
  • 无论客户端给服务器端发送读或写请求,服务器端都需要从客户端读取请求内容,所以在这里,读或写请求的读取就对应了服务器端的读事件

这三类事件是由谁来处理的呢?

  • 首先,连接事件由 acceptor 来处理,负责接收连接;acceptor 在接收连接后,会创建 handler,用于网络连接上对后续读写事件的处理;
  • 其次,读写事件由 handler 处理;
  • 最后,在高并发场景中,连接事件、读写事件会同时发生,所以,我们需要有一个角色专门监听和分配事件,这就是 reactor 角色。当有连接请求时,reactor 将产生的连接事件交由 acceptor 处理;当有读写请求时,reactor 将读写事件交由 handler 处理

事实上,这三个角色都是 Reactor 模型中要实现的功能的抽象。当我们遵循 Reactor 模型开发服务器端的网络框架时,就需要在编程的时候,在代码功能模块中实现 reactor、acceptor 和 handler 的逻辑。

在编程时,我们又该如何实现这三者的交互呢?

所谓的事件驱动框架,就是在实现 Reactor 模型时,需要实现的代码整体控制逻辑。

事件驱动框架包括了两部分:一是事件初始化;二是事件捕获、分发和处理主循环

事件初始化是在服务器程序启动时就执行的,它的作用主要是创建需要监听的事件类型,以及该类事件对应的 handler。而一旦服务器完成初始化后,事件初始化也就相应完成了,服务器程序就需要进入到事件捕获、分发和处理的主循环中。

在开发代码时,我们通常会用一个 while 循环来作为这个主循环。然后在这个主循环中,我们需要捕获发生的事件、判断事件类型,并根据事件类型,调用在初始化时创建好的事件 handler 来实际处理事件。

比如说,当有连接事件发生时,服务器程序需要调用 acceptor 处理函数,创建和客户端的连接。而当有读事件发生时,就表明有读或写请求发送到了服务器端,服务器程序就要调用具体的请求处理函数,从客户端连接中读取请求内容,进而就完成了读事件的处理

Reactor 模型的基本工作机制:客户端的不同类请求会在服务器端触发连接、读、写三类事件,这三类事件的监听、分发和处理又是由 reactor、acceptor、handler 三类角色来完成的,然后这三类角色会通过事件驱动框架来实现交互和事件处理。

如何开发实现一个事件驱动框架呢?

Redis 的网络框架实现了 Reactor 模型,并且自行开发实现了一个事件驱动框架。这个框架对应的 Redis 代码实现文件是ae.c,对应的头文件是ae.h。

事件驱动框架的实现离不开事件的定义,以及事件注册、捕获、分发和处理等一系列操作。当然,对于整个框架来说,还需要能一直运行,持续地响应发生的事件。从 ae.h 头文件中就可以看到,Redis 为了实现事件驱动框架,相应地定义了事件的数据结构、框架主循环函数、事件捕获分发函数、事件和 handler 注册函数。

事件的数据结构定义:以 aeFileEvent 为例

在 Redis 事件驱动框架的实现当中,事件的数据结构是关联事件类型和事件处理函数的关键要素。而 Redis 的事件驱动框架定义了两类事件:IO 事件和时间事件,分别对应了客户端发送的网络请求和 Redis 自身的周期性操作。

aeFileEvent 是一个结构体,它定义了 4 个成员变量 mask、rfileProce、wfileProce 和 clientData

  • mask 是用来表示事件类型的掩码。对于网络通信的事件来说,主要有 AE_READABLE、AE_WRITABLE 和 AE_BARRIER 三种类型事件。框架在分发事件时,依赖的就是结构体中的事件类型;
  • rfileProc 和 wfileProce 分别是指向 AE_READABLE 和 AE_WRITABLE 这两类事件的处理函数,也就是 Reactor 模型中的 handler。框架在分发事件后,就需要调用结构体中定义的函数进行事件处理;
  • 最后一个成员变量 clientData 是用来指向客户端私有数据的指针。

主循环:aeMain 函数

aeMain 函数的逻辑很简单,就是用一个循环不停地判断事件循环的停止标记。如果事件循环的停止标记被设置为 true,那么针对事件捕获、分发和处理的整个主循环就停止了;否则,主循环会一直执行。

aeMain 函数是在哪里被调用的呢?

服务器程序的 main 函数在完成 Redis server 的初始化后,会调用 aeMain 函数开始执行事件驱动框架

事件捕获与分发:aeProcessEvents 函数

aeMain 函数包含了事件框架的主循环,那么在主循环中,事件又是如何被捕获、分发和处理呢?

aeProcessEvents 函数实现的主要功能,包括捕获事件、判断事件类型和调用具体的事件处理函数,从而实现事件的处理。

从 aeProcessEvents 函数的主体结构中,我们可以看到主要有三个 if 条件分支

int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
    int processed = 0, numevents;
    /* 若没有事件处理,则立刻返回*/
    if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;
    /*如果有IO事件发生,或者紧急的时间事件发生,则开始处理*/
    if (eventLoop->maxfd != -1 || ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
    }
    /* 检查是否有时间事件,若有,则调用processTimeEvents函数处理 */
    if (flags & AE_TIME_EVENTS)
        processed += processTimeEvents(eventLoop);
    /* 返回已经处理的文件或时间*/
    return processed; 
}
  • 既没有时间事件,也没有网络事件;
  • 有 IO 事件或者有需要紧急处理的时间事件
  • 只有普通的时间事件。

那么对于第一种情况来说,因为没有任何事件需要处理,aeProcessEvents 函数就会直接返回到 aeMain 的主循环,开始下一轮的循环;而对于第三种情况来说,该情况发生时只有普通时间事件发生,所以 aeMain 函数会调用专门处理时间事件的函数 processTimeEvents,对时间事件进行处理。第二种情况,当该情况发生时,Redis 需要捕获发生的网络事件,并进行相应的处理。那么从 Redis 源码中我们可以分析得到,在这种情况下,aeApiPoll 函数会被调用,用来捕获事件

Redis 是依赖于操作系统底层提供的 IO 多路复用机制,来实现事件捕获,检查是否有新的连接、读写事件发生。

事件注册:aeCreateFileEvent 函数

当 Redis 启动后,服务器程序的 main 函数会调用 initSever 函数来进行初始化,而在初始化的过程中,aeCreateFileEvent 就会被 initServer 函数调用,用于注册要监听的事件,以及相应的事件处理函数,在 initServer 函数的执行过程中,initServer 函数会根据启用的 IP 端口个数,为每个 IP 端口上的网络事件,调用 aeCreateFileEvent,创建对 AE_READABLE 事件的监听,并且注册 AE_READABLE 事件的处理 handler,也就是 acceptTcpHandler 函数

那么,aeCreateFileEvent 如何实现事件和处理函数的注册呢?

Linux 提供了 epoll_ctl API,用于增加新的观察事件。而 Redis 在此基础上,封装了 aeApiAddEvent 函数,对 epoll_ctl 进行调用。所以这样一来,aeCreateFileEvent 就会调用 aeApiAddEvent,然后 aeApiAddEvent 再通过调用 epoll_ctl,来注册希望监听的事件和相应的处理函数。等到 aeProceeEvents 函数捕获到实际事件时,它就会调用注册的函数对事件进行处理了。

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
19天前
|
数据采集 存储 JSON
Python网络爬虫:Scrapy框架的实战应用与技巧分享
【10月更文挑战第27天】本文介绍了Python网络爬虫Scrapy框架的实战应用与技巧。首先讲解了如何创建Scrapy项目、定义爬虫、处理JSON响应、设置User-Agent和代理,以及存储爬取的数据。通过具体示例,帮助读者掌握Scrapy的核心功能和使用方法,提升数据采集效率。
60 6
|
27天前
|
机器学习/深度学习 人工智能
类人神经网络再进一步!DeepMind最新50页论文提出AligNet框架:用层次化视觉概念对齐人类
【10月更文挑战第18天】这篇论文提出了一种名为AligNet的框架,旨在通过将人类知识注入神经网络来解决其与人类认知的不匹配问题。AligNet通过训练教师模型模仿人类判断,并将人类化的结构和知识转移至预训练的视觉模型中,从而提高模型在多种任务上的泛化能力和稳健性。实验结果表明,人类对齐的模型在相似性任务和出分布情况下表现更佳。
57 3
|
7天前
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
21 2
|
8天前
|
运维 网络协议 算法
7 层 OSI 参考模型:详解网络通信的层次结构
7 层 OSI 参考模型:详解网络通信的层次结构
21 1
|
12天前
|
存储 安全 网络安全
网络安全法律框架:全球视角下的合规性分析
网络安全法律框架:全球视角下的合规性分析
24 1
|
20天前
|
数据采集 前端开发 中间件
Python网络爬虫:Scrapy框架的实战应用与技巧分享
【10月更文挑战第26天】Python是一种强大的编程语言,在数据抓取和网络爬虫领域应用广泛。Scrapy作为高效灵活的爬虫框架,为开发者提供了强大的工具集。本文通过实战案例,详细解析Scrapy框架的应用与技巧,并附上示例代码。文章介绍了Scrapy的基本概念、创建项目、编写简单爬虫、高级特性和技巧等内容。
46 4
|
19天前
|
网络协议 算法 网络性能优化
计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议
计算机网络常见面试题(一):TCP/IP五层模型、应用层常见的协议、TCP与UDP的区别,TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议、ARP协议
|
20天前
|
网络协议 物联网 API
Python网络编程:Twisted框架的异步IO处理与实战
【10月更文挑战第26天】Python 是一门功能强大且易于学习的编程语言,Twisted 框架以其事件驱动和异步IO处理能力,在网络编程领域独树一帜。本文深入探讨 Twisted 的异步IO机制,并通过实战示例展示其强大功能。示例包括创建简单HTTP服务器,展示如何高效处理大量并发连接。
39 1
|
24天前
|
机器学习/深度学习 人工智能 算法
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
车辆车型识别,使用Python作为主要编程语言,通过收集多种车辆车型图像数据集,然后基于TensorFlow搭建卷积网络算法模型,并对数据集进行训练,最后得到一个识别精度较高的模型文件。再基于Django搭建web网页端操作界面,实现用户上传一张车辆图片识别其类型。
71 0
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
|
7天前
|
网络协议 Unix Linux
精选2款C#/.NET开源且功能强大的网络通信框架
精选2款C#/.NET开源且功能强大的网络通信框架