Redis队列Stream、Redis多线程详解(三)

news2024/12/23 5:32:43

Redis中的线程和IO模型

什么是Reactor模式 ?

“反应”器名字中”反应“的由来:

“反应”即“倒置”,“控制逆转”,具体事件处理程序不调用反应器,而向反应器注册一个事件处理器,表示自己对某些事件感兴趣,有时间来了,具体事件处理程序通过事件处理器对某个指定的事件发生做出反应;这种控制逆转又称为“好莱坞法则”(不要调用我,让我来调用你)

例如,路人甲去做男士SPA,前台的接待小姐接待了路人甲,路人甲现在只对10000技师感兴趣,但是路人甲去的比较早,就告诉接待小姐,等10000技师上班了或者是空闲了,通知我。等路人甲接到接待小姐通知,做出了反应,把10000技师占住了。

然后,路人甲想起上一次的那个10000号房间不错,设备舒适,灯光暧昧,又告诉前台的接待小姐,我对10000号房间很感兴趣,房间空出来了就告诉我,我现在先和10000这个小姐聊下人生,10000号房间空出来了,路人甲再次接到接待小姐通知,路人甲再次做出了反应。

路人甲就是具体事件处理程序,前台的接待小姐就是所谓的反应器,“10000技师上班了”和“10000号房间空闲了”就是事件,路人甲只对这两个事件感兴趣,其他,比如10001号技师或者10002号房间空闲了也是事件,但是路人甲不感兴趣。

前台的接待小姐不仅仅服务路人甲1人,他还可以同时服务路人乙、丙……..,每个人所感兴趣的事件是不一样的,前台的接待小姐会根据每个人感兴趣的事件通知对应的每个人。

单线程Reactor模式流程

服务器端的Reactor是一个线程对象,该线程会启动事件循环,并使用Acceptor事件处理器关注ACCEPT事件,这样Reactor会监听客户端向服务器端发起的连接请求事件(ACCEPT事件)。

客户端向服务器端发起一个连接请求,Reactor监听到了该ACCEPT事件的发生并将该ACCEPT事件派发给相应的Acceptor处理器来进行处理。建立连接后关注的READ事件,这样一来Reactor就会监听该连接的READ事件了。

当Reactor监听到有读READ事件发生时,将相关的事件派发给对应的处理器进行处理。比如,读处理器会通过读取数据,此时read()操作可以直接读取到数据,而不会堵塞与等待可读的数据到来。

在目前的单线程Reactor模式中,不仅I/O操作在该Reactor线程上,连非I/O的业务操作也在该线程上进行处理了,这可能会大大延迟I/O请求的响应。所以我们应该将非I/O的业务逻辑操作从Reactor线程上卸载,以此来加速Reactor线程对I/O请求的响应。

 单线程Reactor,工作者线程池

与单线程Reactor模式不同的是,添加了一个工作者线程池,并将非I/O操作从Reactor线程中移出转交给工作者线程池来执行。这样能够提高Reactor线程的I/O响应,不至于因为一些耗时的业务逻辑而延迟对后面I/O请求的处理。

但是对于一些小容量应用场景,可以使用单线程模型,对于高负载、大并发或大数据量的应用场景却不合适,主要原因如下:

① 一个NIO线程同时处理成百上千的链路,性能上无法支撑,即便NIO线程的CPU负荷达到100%,也无法满足海量消息的读取和发送;

② 当NIO线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这更加重了NIO线程的负载,最终会导致大量消息积压和处理超时,成为系统的性能瓶颈;

 多Reactor线程模式

Reactor线程池中的每一Reactor线程都会有自己的Selector、线程和分发的事件循环逻辑。

mainReactor可以只有一个,但subReactor一般会有多个。mainReactor线程主要负责接收客户端的连接请求,然后将接收到的SocketChannel传递给subReactor,由subReactor来完成和客户端的通信。

多Reactor线程模式将“接受客户端的连接请求”和“与该客户端的通信”分在了两个Reactor线程来完成。mainReactor完成接收客户端连接请求的操作,它不负责与客户端的通信,而是将建立好的连接转交给subReactor线程来完成与客户端的通信,这样一来就不会因为read()数据量太大而导致后面的客户端连接请求得不到即时处理的情况。并且多Reactor线程模式在海量的客户端并发请求的情况下,还可以通过实现subReactor线程池来将海量的连接分发给多个subReactor线程,在多核的操作系统中这能大大提升应用的负载和吞吐量。

 Redis中的线程和IO概述

Redis 基于 Reactor 模式开发了自己的网络事件处理器 - 文件事件处理器(file event handler,后文简称为 FEH),而该处理器又是单线程的,所以redis设计为单线程模型。

采用I/O多路复用同时监听多个socket,根据socket当前执行的事件来为 socket 选择对应的事件处理器。

当被监听的socket准备好执行accept、read、write、close等操作时,和操作对应的文件事件就会产生,这时FEH就会调用socket之前关联好的事件处理器来处理对应事件。

所以虽然FEH是单线程运行,但通过I/O多路复用监听多个socket,不仅实现高性能的网络通信模型,又能和 Redis 服务器中其它同样单线程运行的模块交互,保证了Redis内部单线程模型的简洁设计。

下面来看文件事件处理器的几个组成部分。

 socket

文件事件就是对socket操作的抽象, 每当一个 socket 准备好执行连接accept、read、write、close等操作时, 就会产生一个文件事件。一个服务器通常会连接多个socket, 多个socket可能并发产生不同操作,每个操作对应不同文件事件。

I/O多路复用程序

I/O 多路复用程序会负责监听多个socket。

尽管文件事件可能并发出现, 但 I/O 多路复用程序会将所有产生事件的socket放入队列, 通过该队列以有序、同步且每次一个socket的方式向文件事件分派器传送socket。

当上一个socket产生的事件被对应事件处理器执行完后, I/O 多路复用程序才会向文件事件分派器传送下个socket, 如下:

I/O多路复用程序的实现

Redis 的 I/O 多路复用程序的所有功能都是通过包装常见的 select、epoll、 evport 和 kqueue 这些 I/O 多路复用函数库实现的。

每个 I/O 多路复用函数库在 Redis 源码中都对应一个单独的文件:

因为 Redis 为每个 I/O 多路复用函数库都实现了相同的 API , 所以 I/O 多路复用程序的底层实现是可以互换的。Redis 在 I/O 多路复用程序的实现源码`ae.c`文件中宏定义了相应规则,使得程序在编译时自动选择系统中性能最高的 I/O 多路复用函数库作为 Redis 的 I/O 多路复用程序的底层实现:性能降序排列。

注:

evport = Solaris 10

epoll = Linux

kqueue = OS X,FreeBSD

select =通常作为fallback安装在所有平台上

Evport,Epoll和KQueue具有 O(1)描述符选择算法复杂度,并且它们都使用内部内核空间内存结构.他们还可以提供很多(数十万个)文件描述符.

除其他外,select最多只能提供 1024个描述符,并且对描述符进行完全扫描(因此每次迭代所有描述符以选择一个可使用的描述符),因此复杂性是 O(n).

文件事件分派器

文件事件分派器接收 I/O 多路复用程序传来的socket, 并根据socket产生的事件类型, 调用相应的事件处理器。

文件事件处理器

服务器会为执行不同任务的套接字关联不同的事件处理器, 这些处理器是一个个函数, 它们定义了某个事件发生时, 服务器应该执行的动作。

Redis 为各种文件事件需求编写了多个处理器,若客户端连接Redis,对连接服务器的各个客户端进行应答,就需要将socket映射到连接应答处理器写数据到Redis,接收客户端传来的命令请求,就需要映射到命令请求处理器从Redis读数据,向客户端返回命令的执行结果,就需要映射到命令回复处理器当主服务器和从服务器进行复制操作时, 主从服务器都需要映射到特别为复制功能编写的复制处理器。

文件事件的类型

I/O 多路复用程序可以监听多个socket的 ae.h/AE_READABLE 事件和 ae.h/AE_WRITABLE 事件, 这两类事件和套接字操作之间的对应关系如下:

当socket可读(比如客户端对Redis执行write/close操作),或有新的可应答的socket出现时(即客户端对Redis执行connect操作),socket就会产生一个AE_READABLE事件。

当socket可写时(比如客户端对Redis执行read操作),socket会产生一个AE_WRITABLE事件。

I/O多路复用程序可以同时监听AE_REABLE和AE_WRITABLE两种事件,要是一个socket同时产生这两种事件,那么文件事件分派器优先处理AE_REABLE事件。即一个socket又可读又可写时, Redis服务器先读后写socket。

总结

最后,让我们梳理一下客户端和Redis服务器通信的整个过程:

Redis启动初始化时,将连接应答处理器跟AE_READABLE事件关联。

若一个客户端发起连接,会产生一个AE_READABLE事件,然后由连接应答处理器负责和客户端建立连接,创建客户端对应的socket,同时将这个socket的AE_READABLE事件和命令请求处理器关联,使得客户端可以向主服务器发送命令请求。

当客户端向Redis发请求时(不管读还是写请求),客户端socket都会产生一个AE_READABLE事件,触发命令请求处理器。处理器读取客户端的命令内容, 然后传给相关程序执行。

当Redis服务器准备好给客户端的响应数据后,会将socket的AE_WRITABLE事件和命令回复处理器关联,当客户端准备好读取响应数据时,会在socket产生一个AE_WRITABLE事件,由对应命令回复处理器处理,即将准备好的响应数据写入socket,供客户端读取。

命令回复处理器全部写完到 socket 后,就会删除该socket的AE_WRITABLE事件和命令回复处理器的映射。

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

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

相关文章

CTA进网测试《5G消息 终端测试方法》标准依据:YDT 3958-2021

GB 21288-2022 强制国标要求变化​ 与GB 21288-2007相比, 新国标主要有以下变化: 1. 增加职业暴露定义: 2. 增加吸收功率密度定义: 3. 增加不同频率、不同人体部位适用的暴露限值: 4. 增加产品说明书的注释&#xff1a…

ArcGIS Pro用户界面

目录 1 功能区 1.1 快速访问工具栏 1.2 自定义快速访问工具栏 1.3 自定义功能区选项 1.3.1 添加组和命令 1.3.2 添加新选项卡 2 视图 3 用户界面排列 ​编辑 4 窗格 4.1 内容窗格 4.2 目录窗格 4.3 目录视图(类似ArcCatalog) 4.4 浏览对话框…

注册表取证

目录 操作系统安装时间 计算机名称 本地用户 最后登录的用户 当前登录用户 U盘序列号 USB挂载的盘符 卷标名称 安装的程序 ​编辑卸载的程序 最近使用的文件 最近运行的命令行 操作系统安装时间 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion…

无需代码!新人可操作!分享20个可视化大屏(内附下载)

做前端开发的各位,大都知晓老板钟爱的可视化大屏—清晰、便捷、撑排面,轻轻松松打破数据孤岛等一系列问题。我真的深有体会,前些天,老板去人家公司开会,回来就把这大屏安排上了。之前就认为大屏也不过是面子工程&#…

03 - 大学生如何使用GPT

大学生如何使用GPT提高学习效率 一、引言 在当今的高速发展的信息时代,大学生面临着越来越多的学习挑战。作为一种先进的人工智能技术,GPT为大学生提供了一种强大的学习工具。本文将介绍大学生在不同场景中如何使用GPT来提高学习效率,并给出…

「区间DP-步入」凸多边形的划分

凸多边形的划分 题目描述 给定一个具有N个顶点的凸多边形,将顶点从1至N标号,每个顶点的权值都是一个正整数。将这个凸多边形划分成N-2个互不相交的三角形,试求这些三角形顶点的权值乘积和至少为多少。 输入描述 输入第一行为顶点数N第二行…

云智慧陆兴海:统一运维体系为数字政府建设保驾护航

2023年4月6日至7日,由长春市人民政府、吉林省政务服务和数字化建设管理局主办的《2023长春数字经济发展论坛》在长春隆重举行。 本次论坛旨在探讨数字经济的理论创新、实践探索和发展路径,推动长春市乃至吉林省的数字化转型和高质量发展。第十二届全国政…

无源滤波器为什么能滤波?

滤波器能够滤波的本质是利用构造特定的阻抗特性引起反射和损耗来实现对频率的选择。 如果从能量守恒的角度来讲,被抑制掉的信号去哪里了?​ 我们先看一下基本电路原理,上图中,负载接收的功率为 我们知道,最大功率传输…

ChatGPT: 从GPT-3.5到GPT-4,探索语言模型的演进之路

ChatGPT: 从GPT-3.5到GPT-4,探索语言模型的演进之路 引言 人工智能语言模型的演进 随着人工智能的快速发展,语言模型作为自然语言处理领域的一项重要技术也在不断演进。从最初的基于规则的系统,到基于统计的模型,再到近年来的深度…

【Scala入门】scala基础语法:to和until,if-else和for循环,while循环

目录: 【Scala入门】Scala下载及安装(Windows)以及Idea创建第一个scala项目_水w的博客-CSDN博客 【Scala入门】scala基础语法:类和对象,变量和常量_水w的博客-CSDN博客 目录 2.4 数据类型 2.5 to和until,…

破防了,这才是机房运维的高效方法

在云计算、5G等新业务野蛮生长的催化下,机房规模与容量也呈倍速扩张。机房安全是业务发展的底座。提升机房设备安全和管理效率,避免人为因素导致的事故发生,是机房运维的必要前提。 安全生产重于泰山,除了日常的科学防护&#xf…

C++函数适配器和函数包装器:让你的函数更灵活

📖作者介绍:22级树莓人(计算机专业),热爱编程<目前在c++阶段>——目标Windows,MySQL,Qt,数据结构与算法,Linux,多线程&…

机器学习——K-Means算法优化(一)代价函数

机器学习——K-Means算法优化(一)代价函数 文章目录 机器学习——K-Means算法优化(一)代价函数一、K-Means算法(代价函数)二、代码部分 在K-Means算法中,对K个质心的选择,容易陷入局…

Java基础之字节流

文章目录 一、字节输入流1.1 字节输入流读取单个字节1.2 字节输入流一个字节一个字节读取数据1.3 字节输入流一个字节数组一个字节数组读取数据 二、字节输入流读出数据乱码问题三、字节输出流3.1 一次向指定文本写入一个字节数据3.2 一次向指定文本写入一个字节数组数据3.3 一…

CentOS7---基于 CentOS 7 构建 LVS-DR 群集

一、对比 LVS 负载均衡群集的 NAT 模式和 DR 模式,比较其各自的优势 NAT模式(地址转换) 原理:就是把客户端发来的数据包的IP头的目的地址,在负载均衡器上换成其中一台RS的IP地址 并发至此RS来处理,RS处理完后把数据交给负载均衡器…

LeetCode热题HOT100:单词拆分、环形链表 II、LRU 缓存

LeetCode热题HOT100 139. 单词拆分 题目:给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。 示例 1&#xf…

[前端基础]Node.js简单操作,手把手教你搭建一个轻量级应答服务器(会继续补充操作细节,欢迎讨论)

注意:前置知识:回调函数,异步,ajax技术,端口 目录 1.什么是node.js 2.模块的概念 3. 回调函数 4.关于文件的管理 5.关于流的操作 6.关于构建服务器:前端部分,如何向后端发送请求 7.后端创建简单的服务器对象 8.后端如何处理请求 9.前端如何处理返回的数据 10.简易应答…

STL标准模板库 vector容器与迭代器入门

STL五大件 标准模板库vector容器:vector 声明初始化vector 容器 :push_backvector 容器 :push_back的问题vector容器:push_back的问题,reserve解决 vector容器:insert函数vector容器:insert函数…

【从零开始玩量化17】如何python+QMT完成自动化交易?(全网最详细入门教程)

一、什么是QMT 此部分为扫盲内容,有一定了解者可以跳过。 概念 它是一款量化交易客户端软件,由一家叫做迅投公司出品,可以直接登录你的券商账号进行股票交易,但与同花顺/通信达不同的是,它暴露了基于python的交易API&…

【人生苦短,我学 Python】进阶篇——异常处理(Day16)

写在前面:大家好!我是【AI 菌】。我热爱AI、热爱分享、热爱开源! 这博客是我对学习的一点总结与记录。如果您也对 深度学习、机器视觉、算法、Python、C 感兴趣,可以关注我的动态,我们一起学习,一起进步~ 我…