Redis的IO多路复用

news2025/4/19 6:24:37

1 传统的socket编码模型

传统 Socket 模型通常采用 多线程/多进程 或 阻塞 I/O 的方式处理网络请求。以下是典型实现步骤:

  1. 创建套接字(Socket)
    步骤:调用 socket() 创建一个 TCP/UDP 套接字。通常把这个套接字称为【主动套接字】(Active Socket)

  2. 绑定本地地址(Bind)
    步骤:将套接字绑定到本地 IP 和端口

struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
server_addr.sin_addr.s_addr = INADDR_ANY;
bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
  1. 监听连接(Listen)
    步骤:调用 listen 函数,将主动套接字转换为【监听套接字】,开始监听客户端的连接。
listen(sockfd, 5);  // 允许最多 5 个等待连接
  1. 接受连接(Accept)
    步骤:通过 accept() 接受新连接,返回新套接字(称为:【已连接套接字】)用于通信。

  2. 处理请求
    关键点:为每个新连接创建独立线程或进程,处理读写操作:
    在这里插入图片描述

  3. 读写数据(阻塞 I/O)
    线程/进程内的操作:
    读取数据:调用 read() 从客户端读取数据(阻塞直到数据到达)。
    在这里插入图片描述


listenSocket = socket(); //调用socket系统调用创建一个主动套接字
bind(listenSocket); //绑定地址和端口
listen(listenSocket); //将默认的主动套接字转换为服务器使用的被动套接字,也就是监听套接字
while (1) {  //循环监听是否有客户端连接请求到来
 connSocket = accept(listenSocket);  //接受客户端连接 
 recv(connsocket);  //从客户端读取数据,只能同时处理一个客户端  
 send(connsocket); //给客户端返回数据,只能同时处理一个客户端
}

虽然它能够实现服务器端和客户端之间的通信,但是程序每调用一次 accept 函数,只能处理一个客户端连接。因此,如果想要处理多个并发客户端的请求,我们就需要使用多线程的方法,来处理通过 accept 函数建立的多个客户端连接上的请求。

listenSocket = socket(); //调用socket系统调用创建一个主动套接字
bind(listenSocket);  //绑定地址和端口
listen(listenSocket); //将默认的主动套接字转换为服务器使用的被动套接字,即监听套接字
while (1) { //循环监听是否有客户端连接到来
   connSocket = accept(listenSocket); //接受客户端连接,返回已连接套接字
   pthread_create(processData, connSocket); //创建新线程对已连接套接字进行处理
   
}

//处理已连接套接字上的读写请求
processData(connSocket){
   recv(connsocket); //从客户端读取数据,只能同时处理一个客户端
   send(connsocket); //给客户端返回数据,只能同时处理一个客户端
}

2:为什么redis不采用传统的socket编程模型

虽然这种方法能提升服务器端的并发处理能力,遗憾的是,Redis 的主执行流程是由一个线程在执行,无法使用多线程的方式来提升并发处理能力。所以,该方法对 Redis 并不起作用。

传统 Socket 模型通常采用 多线程/多进程 或 阻塞 I/O,而 Redis 的多路复用模型在设计上存在以下关键差异:

在这里插入图片描述

3 Redis 多路复用的编程模型实现原理

多路I/O复用模型是指使用一个线程来监控多个文件描述符(fd)的读写状态,当某个fd准备好执行读或写操作时,就通知相应的事件处理器来处理。
多路I/O复用模型 , 就是通过少量线程 监控大量 连接,提升了 线程的利用率,减少了线程阻塞,提高了I/O效率和利用率。避免了阻塞式I/O模型中,单个线程只能等待一个fd的问题。
例如Linux系统中提供了多种多路I/O复用技术的实现方式,如select、poll、epoll等。
select/epoll等IO多路复用技术提供了一种基于事件触发的回调模式,每当有不同事件发生时,Redis能够迅速调用相应的事件处理器,始终保持在处理事件的状态,从而提升了其响应速度。

下面是 IO多路复用技术 的一个基础的 流程图:

在这里插入图片描述

Redis通过使用IO多路复用技术(如epoll、kqueue或select等),在一个线程内同时监听多个socket连接,当有网络事件发生时(如读写就绪),再逐一处理。
由于Redis线程并不会因为等待某个特定socket的IO操作完毕而停滞,它可以流畅地在多个客户端间切换,即时响应每个客户端的不同请求,从而实现在单线程环境下对大量并发连接的有效处理和高并发性能。
这样可以处理大量并发连接,并在单线程中高效地调度网络事件,使得单线程也能应对高并发场景。
所以Redis服务端,整体来看,就是一个以事件驱动的程序。
它的操作都是基于事件的方式进行的。客户端请求被分发给文件事件分派器,再由事件处理器处理。

在这里插入图片描述
在Redis中,事件驱动架构通过监听和处理各种网络I/O事件以及定时事件,使得Redis服务端能够在一个线程内高效地服务于多个客户端连接,并执行相关的命令操作。

在这里插入图片描述

1. 事件循环(Event Loop)

Redis 的主线程通过一个无限循环(Event Loop)持续监听所有注册的 socket 的事件(如可读、可写、超时等)。当事件发生时(如客户端发送请求),事件循环会触发对应的回调函数处理该事件。

2. 系统级多路复用技术

Redis 使用操作系统提供的多路复用 API(如 epoll(Linux)、kqueue(BSD/macOS)、select/poll(通用))来高效管理大量 socket 连接:

epoll(Linux):

水平触发(LT, Level Triggered):当 socket 可读时,只要事件未被处理,每次 epoll_wait 都会返回该事件。
边缘触发(ET, Edge Triggered):当 socket 可读时,只有第一次 epoll_wait 会返回该事件,后续需确保数据完全处理。
Redis 默认使用 epoll 的 边缘触发模式,避免重复触发事件。

在这里插入图片描述

kqueue(BSD/macOS):

提供更细粒度的事件控制,支持同时监听读、写、错误等事件。
Redis 在 macOS 上默认使用 kqueue。

3. 事件处理流程

注册事件:将 socket 的读/写事件注册到多路复用器(如 epoll)中。
等待事件:主线程通过 epoll_wait(或 kqueue 的 kevent)阻塞等待事件发生。
处理事件:当事件触发时,主线程依次处理每个事件(如读取客户端请求、写入响应)。
循环执行:重复上述步骤,形成事件循环。

4. 单线程模型

Redis 是单线程的,所有网络 I/O 和命令处理都在同一个线程中顺序执行。通过多路复用技术,单线程可以高效处理大量连接,避免了多线程的上下文切换开销。即多路复用复用的其实是这个单线程。

4 Redis 多路复用的实现细节

1. 网络 I/O 框架

Redis 的网络模块(networking.c)封装了多路复用逻辑:

事件注册:通过 aeApiAddEvent 将 socket 的读/写事件注册到 epoll/kqueue。
事件循环:aeMain 函数是事件循环的核心,持续调用多路复用 API 等待事件。
回调处理:事件触发后,调用对应的回调函数(如 readQueryFromClient 处理客户端请求)。

2. 非阻塞 I/O

Redis 的所有 I/O 操作(如读取客户端请求、写入响应)均采用非阻塞模式:

读操作:当 epoll 触发可读事件时,调用 read 读取数据,直到缓冲区为空或无法读取。
写操作:当 epoll 触发可写事件时,将响应数据写入发送缓冲区,直到数据全部发送或缓冲区满。

3. 时间事件(Timeout)

Redis 还支持时间事件(如键过期、持久化触发等),通过 aeCreateTimeEvent 注册,并在事件循环中处理。

5、优缺点对比

在这里插入图片描述

6、 适用场景

在这里插入图片描述

7:问题环节,概念澄清

问题1:

等待事件:主线程通过 epoll_wait(或 kqueue 的 kevent)阻塞等待事件发生。
Redis 的所有 I/O 操作(如读取客户端请求、写入响应)均采用非阻塞模式:
这2句是不矛盾,前面说阻塞,后面说非阻塞。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

问题2:

主线程调用 epoll_wait 时会主动进入阻塞状态,等待事件发生(如某个套接字变为可读或可写)。 那么事件一直没有发生,岂不是就完全阻塞等死了。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2336568.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于YOLOv9的课堂行为检测系统

基于YOLOv9的课堂行为检测系统 项目概述 本项目是一个基于YOLOv9深度学习模型的课堂行为检测系统,旨在通过计算机视觉技术自动识别和监测课堂中学生的各种行为状态,帮助教师更好地了解课堂教学效果。 项目结构 课堂行为检测/ ├── data/ │ ├──…

端、管、云一体化原生安全架构 告别外挂式防护!

面对数字化转型浪潮,企业网络安全风险日益凸显。数据泄露、黑客勒索等事件频发,合规要求加速推进。尽管企业纷纷部署了防病毒、身份认证、文件加密、入侵防护、流量监控等多种安全系统,但分散且孤立的架构非但没有有效抵御风险,反…

BI面向模型开发和面向报表开发,有什么区别?

在数字化时代,商业智能(BI)已成为企业决策不可或缺的工具。BI项目实施时,通常有两种开发模式:面向模型开发和面向报表开发。虽然两者都旨在通过数据驱动决策,但在开发逻辑、目标价值和技术路径上存在显著差…

进程控制(上)【Linux操作系统】

进程控制 写时拷贝 本质是一种减少深拷贝的方法 Linux中有很多拷贝的场景都用得上写时拷贝,下面以创建子进程时的写时拷贝为例: 子进程被创建的时候: 会继承父进程的mm_struct和页表 所以子进程刚刚继承时,父子进程的代码和数据…

5G网络下客户端数据业务掉线频繁

上层应用的日志和界面在待机状态下(即没有做通话等业务操作),会频繁提示“离线”。 主要先看有没有丢网,UL BLER有没有问题。确认没有问题。看到业务信道释放后也可以成功重新建链。所以以为这个只是终端业务进入dormant态的提示…

【Docker项目实战】使用Docker部署Gitblit服务器

【Docker项目实战】使用Docker部署Gitblit服务器 一、Gitblit介绍1.1 Gitblit 介绍1.2 主要特点 二、本次实践规划2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Gitblit镜像五、部署Gitbli…

Vitis: 使用自定义IP时 Makefile错误 导致编译报错

参考文章: 【小梅哥FPGA】 Vitis开发中自定义IP的Makefile路径问题解决方案 Vitis IDE自定义IP Makefile错误(arm-xilinx-eabi-gcc.exe: error: *.c: Invalid argument)解决方法 Vitis 使用自定义IP时: Makefile 文件里的语句是需要修改的,…

helm的go模板语法学习

1、helm chart 1.0、什么是helm? 介绍:就是个包管理器。理解为java的maven、linux的yum就好。 安装方法也可参见官网: https://helm.sh/docs/intro/install 通过前面的演示我们知道,有了helm之后应用的安装、升级、查看、停止都…

AI 语音公司 ElevenLabs 进军亚太市场设立东京子公司;EverTutor Live :语音交互 AI 教育平台丨日报

开发者朋友们大家好: 这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的 技术 」、「有亮点的 产品 」、「有思考的 文章 」、「有态度的 观…

STM32启动流程详解

STM32启动流程详解 本文档详细介绍STM32微控制器从上电到main函数执行的完整启动流程。 1. 上电与复位过程 当STM32芯片上电或复位时,硬件会执行以下步骤: 上电复位(POR)/低电平复位(PDR): 芯片接通电源或NRST引脚置低时触发初始PC值设置: 程序计数器…

Langchain + Gemini API调用基本操作

本文参考Langchain中ChatGoogleGenerativeAI的官方文档,在本地的jupyter notebook中运行。 关于API的细节在官方文档最开头给出: 我们在使用时,可以选择model"gemini-2.0-flash-001"或者生成图片的ChatGoogleGenerativeAI(model“…

【数据结构】4.单链表实现通讯录

在上一篇文章我们学会了用单链表来实现各种方法,在这一篇文章我们将在单链表的基础上实现通讯录。 0、准备工作 实现通讯录之前,我们还需要在单链表的基础上添加2个文件,头文件Contact.h和源文件Contact.c。Contact.c来实现通讯录方法的声明…

接口自动化测试(一)

一、HTTP请求的核心概念及原理详解 HTML:超文本标记语言-----通过<标记符>内容</标记符>格式-------页面 URL:统一资源定位符 返回数据有很多&#xff1a;页面、图片、视频&#xff0c;都可以进行返回---统称为&#xff1a;资源HTTP:超文本传输协议(请求-响应的协…

【JavaEE】Spring AOP的注解实现

目录 一、AOP 与 Spring AOP二、Spring AOP简单实现三、详解Spring AOP3.1 Spring AOP 核心概念3.1.1 切点&#xff08;Pointcut&#xff09;3.1.2 连接点&#xff08;Join Point&#xff09;3.1.3 通知&#xff08;Advice&#xff09;3.1.4 切面&#xff08;Aspect&#xff09…

揭秘大数据 | 22、软件定义存储

揭秘大数据 | 19、软件定义的世界-CSDN博客 揭秘大数据 | 20、软件定义数据中心-CSDN博客 揭秘大数据 | 21、软件定义计算-CSDN博客 老规矩&#xff0c;先把这个小系列的前三篇奉上。今天书接上文&#xff0c;接着叙软件定义存储的那些事儿。 软件定义存储源于VMware公司于…

OpenCV 图形API(37)图像滤波-----分离过滤器函数sepFilter()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 应用一个可分离的线性滤波器到一个矩阵&#xff08;图像&#xff09;。 该函数对矩阵应用一个可分离的线性滤波器。也就是说&#xff0c;首先&a…

flutter下载SDK环境配置步骤详解

目录 1.Flutter官网地址、SDK下载地址? 1.1 选择你电脑的系统​ 2.配置环境 3.解决环境报错 zsh:command not found:flutter 1.Flutter官网地址、SDK下载地址? flutter官网地址: URL 1.1 选择你电脑的系统 下载解压动目录就OK了 2.配置环境 1、打开命令行&#xf…

数据结构与算法入门 Day 0:程序世界的基石与密码

&#x1f31f;数据结构与算法入门 Day 0&#xff1a;程序世界的基石与密码&#x1f511; ps&#xff1a;接受到了不少的私信反馈&#xff0c;说应该先把前置的知识内容做一个梳理&#xff0c;所以把昨天的文章删除了&#xff0c;重新开启今天的博文写作 Hey 小伙伴们&#xff…

vscode终端运行windows服务器的conda出错

远程windows服务器可以运行&#xff0c;本地vscode不能。 打开vscode settings.json文件 添加conda所在路径

“大湾区珠宝艺境花园”璀璨绽放第五届消博会

2025年4月13日&#xff0c;第五届中国国际消费品博览会&#xff08;以下简称"消博会"&#xff09;重要主题活动——《大湾区珠宝艺境花园》启动仪式在海南国际会展中心2号馆隆重举行。由广东省金银珠宝玉器业厂商会组织带领粤港澳大湾区优秀珠宝品牌&#xff0c;以“…