Redis:IO多路复用深度解析

news2024/9/9 7:48:38

目录

  • 一、Multiplexing(IO多路复用)
    • 1.是什么
    • 2.能干嘛
    • 3.Reactor设计模式
    • 4.select, poll, epoll 都是I/O多路复用的具体的实现
      • 4.1 select方法
      • 4.2 poll方法
      • 4.3 epoll方法
      • 4.4 三个方法对比
    • 5.5种 I/O 模型总结
    • 6.为什么3个都保有

一、Multiplexing(IO多路复用)

IO multiplexing就是我们说的select,poll,epoll,有些地方也称这种IO方式为event driven IO事件驱动IO。就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。可以基于一个阻塞对象,同时在多个描述符上等待就绪,而不是使用多个线程(每个文件描述符一个线程,每次new一个线程),这样可以大大节省系统资源。所以,I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。
在这里插入图片描述

1.是什么

词牌
在这里插入图片描述
模型
I/O多路复用在英文中其实叫 I/O multiplexing
在这里插入图片描述
多个Sock复用一根网线这个功能是在内核+驱动层实现的
I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态来同时管理多个I/O流. 目的是尽量多的提高服务器的吞吐能力。
在这里插入图片描述
大家都用过nginx,nginx使用epoll接收请求,ngnix会有很多链接进来, epoll会把他们都监视起来,然后像拨开关一样,谁有数据就拨向谁,然后调用相应的代码处理。redis类似同理
FileDescriptor
文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。
在这里插入图片描述

模拟一个tcp服务器处理30个客户socket。
假设你是一个监考老师,让30个学生解答一道竞赛考题,然后负责验收学生答卷,你有下面几个选择:

第一种选择:按顺序逐个验收,先验收A,然后是B,之后是C、D。。。这中间如果有一个学生卡住,全班都会被耽误,你用循环挨个处理socket,根本不具有并发能力。

第二种选择:你创建30个分身线程,每个分身线程检查一个学生的答案是否正确。 这种类似于为每一个用户创建一个进程或者线程处理连接。

第三种选择,你站在讲台上等,谁解答完谁举手。这时C、D举手,表示他们解答问题完毕,你下去依次检查C、D的答案,然后继续回到讲台上等。此时E、A又举手,然后去处理E和A。。。这种就是IO复用模型。Linux下的select、poll和epoll就是干这个的。

将用户socket对应的fd注册进epoll,然后epoll帮你监听哪些socket上有消息到达,这样就避免了大量的无用操作。此时的socket应该采用非阻塞模式。这样,整个过程只在调用select、poll、epoll这些调用的时候才会阻塞,收发客户消息是不会阻塞的,整个进程或者线程就被充分利用起来,这就是事件驱动,所谓的reactor反应模式。

2.能干嘛

Redis单线程如何处理那么多并发客户端连接,为什么单线程,为什么快

Redis的IO多路复用
Redis利用epoll来实现IO多路复用,将连接信息和事件放到队列中,一次放到文件事件分派器,事件分派器将事件分发给事件处理器。
在这里插入图片描述

  • Redis 是跑在单线程中的,所有的操作都是按照顺序线性执行的,但是由于读写操作等待用户输入或输出都是阻塞的,所以 I/O 操作在一般情况下往往不能直接返回,这会导致某一文件的 I/O 阻塞导致整个进程无法对其它客户提供服务,而 I/O 多路复用就是为了解决这个问题而出现

  • 所谓 I/O 多路复用机制,就是说通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。这种机制的使用需要 select 、 poll 、 epoll 来配合。多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象上等待,无需阻塞等待所有连接。当某条连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理。

  • Redis 服务采用 Reactor 的方式来实现文件事件处理器(每一个网络连接其实都对应一个文件描述符)
    Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:

    • 多个套接字、
    • IO多路复用程序、
    • 文件事件分派器、
    • 事件处理器。

因为文件事件分派器队列的消费是单线程的,所以Redis才叫单线程模型

3.Reactor设计模式

是什么
基于 I/O 复用模型:多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象上等待,无需阻塞等待所有连接。当某条连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理。
Reactor 模式,是指通过一个或多个输入同时传递给服务处理器的服务请求的事件驱动处理模式。服务端程序处理传入多路请求,并将它们同步分派给请求对应的处理线程,Reactor 模式也叫 Dispatcher 模式。即 I/O 多了复用统一监听事件,收到事件后分发(Dispatch 给某进程),是编写高性能网络服务器的必备技术。
在这里插入图片描述
redis为什么是单线程
在这里插入图片描述
Redis 服务采用 Reactor 的方式来实现文件事件处理器(每一个网络连接其实都对应一个文件描述符)

4.select, poll, epoll 都是I/O多路复用的具体的实现

所谓 I/O 多路复用机制指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程,就是说通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。这种机制的使用需要 select 、 poll 、 epoll 来配合。
多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象上等待,无需阻塞等待所有连接。
当某条连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理。

4.1 select方法

Linux官网或者man
https://man7.org/linux/man-pages/man2/select.2.html
select是第一个实现 (1983 左右在BSD里面实现)
在这里插入图片描述
select 函数监视的文件描述符分3类,分别是readfds、writefds和exceptfds,将用户传入的数组拷贝到内核空间
调用后select函数会阻塞,直到有描述符就绪(有数据 可读、可写、或者有except)或超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。
当select函数返回后,可以通过遍历fdset,来找到就绪的描述符。
C语言代码
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
优点
select 其实就是把NIO中用户态要遍历的fd数组(我们的每一个socket链接,安装进ArrayList里面的那个)拷贝到了内核态,让内核态来遍历,因为用户态判断socket是否有数据还是要调用内核态的,所有拷贝到内核态后,这样遍历判断的时候就不用一直用户态和内核态频繁切换了
从代码中可以看出,select系统调用后,返回了一个置位后的&rset,这样用户态只需进行很简单的二进制比较,就能很快知道哪些socket需要read数据,有效提高了效率

在这里插入图片描述
问题

  • 1、bitmap最大1024位,一个进程最多只能处理1024个客户端

  • 2、&rset不可重用,每次socket有数据就相应的位会被置位

  • 3、文件描述符数组拷贝到了内核态(只不过无系统调用切换上下文的开销。(内核层可优化为异步事件通知)),仍然有开销。select 调用需要传入 fd 数组,需要拷贝一份到内核,高并发场景下这样的拷贝消耗的资源是惊人的。(可优化为不复制)

  • 4、select并没有通知用户态哪一个socket有数据,仍然需要O(n)的遍历。select 仅仅返回可读文件描述符的个数,具体哪个可读还是要用户自己遍历。(可优化为只返回给用户就绪的文件描述符,无需用户做无效的遍历)

select小结论
select方式,既做到了一个线程处理多个客户端连接(文件描述符),又减少了系统调用的开销(多个文件描述符只有一次 select 的系统调用 + N次就绪状态的文件描述符的 read 系统调用

4.2 poll方法

Linux官网或者man
https://man7.org/linux/man-pages/man2/poll.2.html
1997年实现了poll
在这里插入图片描述
C语言代码
在这里插入图片描述
在这里插入图片描述
优点
1、poll使用pollfd数组来代替select中的bitmap,数组没有1024的限制,可以一次管理更多的client。它和 select 的主要区别就是,去掉了 select 只能监听 1024 个文件描述符的限制。
2、当pollfds数组中有事件发生,相应的revents置位为1,遍历的时候又置位回零,实现了pollfd数组的重用
问题
poll 解决了select缺点中的前两条,其本质原理还是select的方法,还存在select中原来的问题
1、pollfds数组拷贝到了内核态,仍然有开销
2、poll并没有通知用户态哪一个socket有数据,仍然需要O(n)的遍历

4.3 epoll方法

Linux官网或者man
https://man7.org/linux/man-pages/man7/epoll.7.html
在2002年被大神 Davide Libenzi (戴维德·利本兹)发明出来了
三步调用
epoll_create
创建一个 epoll 句柄
在这里插入图片描述

epoll_ctl
向内核添加、修改或删除要监控的文件描述符
在这里插入图片描述
epoll_wait
类似发起了select()
在这里插入图片描述
C语言代码
在这里插入图片描述
在这里插入图片描述
事件通知机制
1、当有网卡上有数据到达了,首先会放到DMA(内存中的一个buffer,网卡可以直接访问这个数据区域)中
2、网卡向cpu发起中断,让cpu先处理网卡的事
3、中断号在内存中会绑定一个回调,哪个socket中有数据,回调函数就把哪个socket放入就绪链表中
结论
多路复用快的原因在于,操作系统提供了这样的系统调用,使得原来的 while 循环里多次系统调用,
变成了一次系统调用 + 内核层遍历这些文件描述符。
epoll是现在最先进的IO多路复用器,Redis、Nginx,linux中的Java NIO都使用的是epoll。
这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。
1、一个socket的生命周期中只有一次从用户态拷贝到内核态的过程,开销小
2、使用event事件通知机制,每次socket中有数据会主动通知内核,并加入到就绪链表中,不需要遍历所有的socket

在多路复用IO模型中,会有一个内核线程不断地去轮询多个 socket 的状态,只有当真正读写事件发送时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有真正有读写事件进行时,才会使用IO资源,所以它大大减少来资源占用。多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。 采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈

4.4 三个方法对比

在这里插入图片描述

5.5种 I/O 模型总结

多路复用快的原因在于,操作系统提供了这样的系统调用,使得原来的 while 循环里多次系统调用,
变成了一次系统调用 + 内核层遍历这些文件描述符。

所谓 I/O 多路复用机制,就是说通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。这种机制的使用需要 select 、 poll 、 epoll 来配合。多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象上等待,无需阻塞等待所有连接。当某条连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理;
所谓 I/O 多路复用机制,就是说通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。这种机制的使用需要 select 、 poll 、 epoll 来配合。多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象上等待,无需阻塞等待所有连接。当某条连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理;

在这里插入图片描述

6.为什么3个都保有

在这里插入图片描述

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

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

相关文章

关于如何使用 python 下载 json 格式数据

本章节内容节自《python编程从入门到实践》第十六章,我们将从网络上下载数据,并对数据进行可视化。就可以对其进行分析甚至观察其规律和关联。 学习目标 我们将访问并可视化以下两种常见格式存储的数据: CSV 使用 Python 模块 CSV 来处理以 C…

SpringBoot整合EasyExcel 3.x

文章目录 1 EasyExcel 3.x1.1 简介1.2 引入依赖1.3 简单导出1.3.1 定义实体类1.3.2 自定义转换器1.3.3 定义接口 1.4 简单导入1.5 复杂导出1.5.1 引言1.5.2 自定义注解1.5.3 定义实体类1.5.4 数据映射与平铺1.5.5 自定义单元格合并策略1.5.6 定义接口 1 EasyExcel 3.x 1.1 简介…

string容器语法

文章目录 string容器string基本概念本质:string和char * 区别:特点: string构造函数示例 string赋值操作示例: string字符串拼接示例: string查找和替换示例: string字符串比较示例: string字符…

第六章 rabbitmq高可用集群

在服务之间会采用mq进行消息通信,而rabbitmq本身也如同consul一样,如果只有一个节点那么就可能出现宕机的问题,并且基于mq的特点我们是可以在多个服务之间使用同一个mq来相互通信,因此高可用的架构设计就必不可少 1、rabbitmq集群方案 主备 远程 镜像 多活 构建 2、…

[uni-app]设置运行到微信小程序

1、设置微信小程序开发工具路径 2、检查微信小程序开发工具是否开启了服务端口 服务端口要是没有开启,会报 initialize。 3、在uni-app开发工具中点击运行微信开发者工具,微信开发工具运行成功。

Spark架构体系

StandAlone模式是spark自带的集群运行模式,不依赖其他的资源调度框架,部署起来简单。 StandAlone模式又分为client模式和cluster模式,本质区别是Driver运行在哪里,如果Driver运行在SparkSubmit进程中就是Client模式,如…

谷歌Med-PaLM 2霸榜医学问答领域

谷歌IO大会上,谷歌CEO桑达尔・皮查伊(Sundar Pichai)向全世界AI开发者发布了谷歌最新的大型语言模型(LLMs)PaLM 2,作为对标OpenAI最新大模型GPT-4的竞品,PaLM 2展现出了强大的多语言和推理能力。…

概率论与数理统计发展历史简介

概率论与数理统计发展历史简介 1 介绍1.1 概述1.2 概率论发展历史1.3 统计学发展历史1.4 概率论演化 2 在线学习在线 概率与统计 视觉化学习 -- 布朗大学何志坚老师的数理统计讲义鸢尾花书--统计至简 参考 1 介绍 1.1 概述 概率论是与概率有关的数学分支。虽然有几种不同的概…

Linux内核模块开发 第 8 章

The Linux Kernel Module Programming Guide Peter Jay Salzman, Michael Burian, Ori Pomerantz, Bob Mottram, Jim Huang译 断水客(WaterCutter)源 LKMPG 8 sysfs: 与模块交互 sysfs 允许用户通过读写模块中的变量实现与内核模块的交互。这个特性在…

leetcode907. 子数组的最小值之和(单调栈-java)

子数组的最小值之和 leetcode907. 子数组的最小值之和题目描述单调栈解法一代码演示单调栈解法二 单调栈专题 leetcode907. 子数组的最小值之和 来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/sum-of-subarray-minimums…

如何快速学习一门计算机语言

如何快速学习一门计算机语言 掌握一门语言的基本数据类型和基本语法。掌握语言里数组和集合工具类的使用掌握循环分支控制掌握一下该计算机语言面向对象或者函数式编程的特征对异常或者错误的处理文件读写,输入输出流字符串的处理日志的打印运行时module或者librar…

【C语言基础】函数

C语言中的函数是模块化编程的基础,通过函数的定义、实参与形参的传递以及函数的调用流程,我们可以实现代码的重用和逻辑的封装。本文将深入探讨C语言函数的定义方式、实参与形参的传递机制,以及函数的调用流程和局部变量与栈内存的关系。 一、…

企业快递管理制定教程

在经济飞速发展的助力之下,现代企业接触到的制度越来越多,除了我们熟知的CRM、OA等等,管理制度进一步细分。企业寄件在企业内部运转中的地位越发重要,随之也产生了快递管理制度。不少人就会问:有必要这么细分吗&#x…

跨应用连接同一个redis,从redis取缓存,对象属性值都为null

本地idea部署和docker部署问题,连接同一个redis,idea项目的redis缓存,docker中取不到,docker中缓存的redis本地取不到 ✅ 原因:idea本地代码实体类未进行代码混淆,docker代码实体类进行了混淆,…

Caused by: java.io.IOException: CreateProcess error=206, 文件名或扩展名太长

java.io.IOException: Cannot run program "D:\javaAPP\jdk\bin\java.exe" (in directory "D:\java\demo"): CreateProcess error206, 文件名或扩展名太长。 Caused by: java.io.IOException: CreateProcess error206, 文件名或扩展名太长。 删除项目.ide…

Vue -- 生命周期 数据共享

1 组件的生命周期 1.1 生命周期 & 生命周期函数 生命周期(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。 生命周期函数:是由 vue 框架提供的内置函数,会伴随着组件的生命…

leetcode极速复习版-第一章数组

目录 数组 数组理论基础 704二分查找 27移除元素 977.有序数组的平方 209.长度最小的子数组 59.螺旋矩阵II 数组部分总结 数组 数组理论基础 数组的元素是不能删的,只能覆盖。 二维数组: 704二分查找 二分法 middle int(left right)的int 直接对着一个…

SSM学习笔记-------SpringMVC(一)

SSM学习笔记-------SpringMVC_day01 SpringMVC_day011、SpringMVC简介1.1 SpringMVC概述 2、SpringMVC入门案例2.1 需求分析2.2 案例制作步骤1:创建Maven项目,并导入对应的jar包步骤2:创建控制器类步骤3:创建配置类步骤4:创建Tomcat的Servlet容器配置类步骤5:配置To…

【2022吴恩达机器学习课程实验翻译笔记】 Python 和 Jupyter Notebook 简介

为了看着比较连贯,我直接翻译了,不放英文原文对照了 选修实验课: Python 和 Jupyter Notebook 简介 欢迎来到第一节选修实验课 选修实验课的目的是: 提供信息,就像这个notebook一样通过实例加深对课程的理解展示在课程中使用的…

【Unity实战】制作类元气骑士、挺进地牢——俯视角射击游戏多种射击效果(二)(附源码)

文章目录 前言一、火箭筒1. 编写火箭筒脚本2. 创建火箭弹和新爆炸特效的预制体3. 编写火箭弹脚本4. 设置好火箭弹和火箭筒的脚本和参数5. 运行效果 二、激光枪1. 编写激光枪脚本2. 先运行游戏,看看效果3. 美化射线4. 完善代码5. 再次运行游戏6. 升级URP项目7. 后处理…