【Redis】IO多路复用机制

news2024/11/24 15:58:49

IO多路复用的概念

IO多路复用其实一听感觉很高大上,但是如果细细的拆分以下,
IO:网络IO,操作系统层面指数据在内核态和用户态之间的读写操作。
多路:多个客户端连接(连接就是套接字描述符,即Socket)
复用:用一个或多个连接处理
其实就是用一个服务端连接进行处理多客户端的请求。实际就是一个服务端进程处理多个套接字描述符,实现返回有select、poll、epoll。

那么什么是文件描述符呢?
说白了就是非负整数,当打开或者创建一个文件描述符时返回一个数值。

整个流程是什么样的?
当用户有请求进来之后,会将用户socket文件描述符注册进入epoll,然后epoll监听哪些socket有消息到达。可以避免Redis主线程来回进行切换或者被阻塞。通过一个主线程来控制请求数据量的转发。
在这里插入图片描述
为什么这样的方式吞吐量比较高呢,其实如果是来一个请求创建一个线程,那么太耗费资源,但是如果一个线程轮询处理,那么可能会被阻塞导致吞吐量较低。

通信方式

同步:调用者需要等待下游系统的结果,线程一直会在等待中。比如订单系统调用支付系统,需要支付系统返回结果才可以进行后续的订单状态修改。处理时间比较快的系统推荐使用。
异步:调用者接受到被调用者的相应,就处理别的事情,一般需要被调用者通过回调函数或者异步MQ的返回方式将结果写回,这种方式对于处理比较耗时的系统来说,一般采用异步方式。
阻塞:调用方会被阻塞,一直什么不干。
非阻塞:调用方不会阻塞,先返回做别的事情。
同步异步:在于被调用方返回消息的通知方式上
阻塞非阻塞:在于调用方等待时候的行为

五种网络编程的IO模型
Blocking IO: 阻塞IO
NoneBlocking IO : 非阻塞IO
IO multiplexing : IO多路复用
singal driven Io 信用驱动IO
saynchronuns 异步IO

BIO

服务端Code

	 public static void main(String[] args) throws IOException {
        byte [] bytes = new byte[1024];
        ServerSocket serverSocket = new ServerSocket(6379);

        while (true) {
            System.out.println("1.建立连接");
            Socket accept = serverSocket.accept();
            System.out.println("2.连接成功");
            InputStream inputStream = accept.getInputStream();
            int length = -1;
            System.out.println("3.等待读取数据");

            while ((length =inputStream.read(bytes)) != -1) {
                System.out.println("4.读取到数据");
                System.out.println(new String(bytes));
            }
            System.out.println("5.数据读取结束");
            inputStream.close();
            accept.close();
            System.out.println("6.关闭资源结束");
        }
    }

客户端

	public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",6379);

        OutputStream outputStream = socket.getOutputStream();

        while (true) {
            Scanner scanner = new Scanner(System.in);
            String next = scanner.next();
            if (next.equalsIgnoreCase("quit")) {
                break;
            }
            outputStream.write(next.getBytes());
            System.out.println("写入数据成功");

            outputStream.close();
            socket.close();
        }
    }

可以发现如果服务端采用建立连接之后,客户端迟迟不写入数据,那么客户端就会一直阻塞在read()中。
那么这种问题如何解决呢,一般简单的就是使用创建多个线程的方式来解决read阻塞问题。

	public static void main(String[] args) throws IOException {
        byte [] bytes = new byte[1024];
        ServerSocket serverSocket = new ServerSocket(6379);

        while (true) {
            System.out.println("1.建立连接");
            Socket accept = serverSocket.accept();
            System.out.println("2.连接成功");

            //多个线程处理read数据读取
            new Thread(()-> {
                InputStream inputStream = null;
                try {
                    inputStream = accept.getInputStream();
                    int length = -1;
                    System.out.println("3.等待读取数据");

                    while ((length =inputStream.read(bytes)) != -1) {
                        System.out.println("4.读取到数据");
                        System.out.println(new String(bytes));
                    }
                    System.out.println("5.数据读取结束");
                    inputStream.close();
                    accept.close();
                    System.out.println("6.关闭资源结束");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

但是如果细心的同学就会发现,其实如果大量的创建线程,会十分消耗系统资源,并且进程内创建线程是有一定的上限,所以解决办法要么使用线程池进行复用,要么使用非read阻塞模式,也就是NIO。
在这里插入图片描述

NIO

通过上面分析可以知道,其实BIO主要是在read过程中读取数据会被阻塞,而NIO通过轮询的方式不断查询数据,但是这样其实也会频繁的空跑CPU。

	public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",6379);

        OutputStream outputStream = socket.getOutputStream();

        while (true) {
            Scanner scanner = new Scanner(System.in);
            String next = scanner.next();
            if (next.equalsIgnoreCase("quit")) {
                break;
            }
            outputStream.write(next.getBytes());
            System.out.println("写入数据成功");

            outputStream.close();
            socket.close();
        }
    }

在这里插入图片描述

Reactor模式

在这里插入图片描述
Reactor模式其实就是将请求处理和分发进行职责划分,一个线程负责请求的转发,而具体的业务逻辑由不同的处理现场进行处理。

select

在这里插入图片描述
可以看到select监听的文件描述符包括三个readfds、writefds、exceptfds,将用户传入的数组拷贝到内核空间,select会被阻塞,直到描述符就绪,返回。

selcet核心执行流程
1.select是一个阻塞函数,当没有数据时,会一直阻塞在select。
2.当有数据时,会将对应的rset设置为1
3.select函数返回,不在阻塞。遍历文件描述符判断那个fd置位,读取数据,然后处理。

优点 :说白了select的核心其实还是将用户态的轮询搬到了内核态,这样可以避免频繁的上下文切换,执行时间和效率上肯定更快。
缺点:
1.rset位不可重复用,每次socket有数据就会相应的位被置位。
2.bitmap 最大1024 一个进程最多处理1024个客户端。
3.文件描述符数组拷贝到了内核态,select调用需要传入fd数组,需要拷贝一份到内核,高并发场景下消耗的资源是惊人的。
4.select 没有通知用户态哪一个socket有数据,需要O(N)遍历。

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

poll

执行流程

在这里插入图片描述
优点
1.使用数组来解决select的bitmap 1024限制。
2.有事件发生时,将对应的revents置位位为1,遍历的时候将对应的位置设置为0,可以实现重用。
缺点
1.poll fds数组拷贝到内核态,仍然有开销。
2.poll并没有通知用户态那个socket有数据,需要O(N)遍历

epoll

在这里插入图片描述
1.epoll_create : 创建一个epoll 句柄
2.epoll_ctl 向内核添加、修改或删除要监听的文件描述符
3.epoll_wait 类似发起select调用

在这里插入图片描述
总结:IO多路复用快的原因在于,本身是用户态到内核态的多次数据调用,进一步优化成一次用户态+内核层遍历文件描述符。
在这里插入图片描述

小结

本篇主要介绍了IO多路复用的机制,从IO模型,通信方式(同步、异步),调用方是否等待(阻塞、非阻塞) ,以及介绍了三种主要的IO模型(BIO、NIO、IO多路复用机制)
而IO多路复用机制是很多中间件核心原理,比如Nginx、Redis等。具体就是三种不同的内置函数,select、poll、epoll,核心就是原来在用户态的while(true)多次调用,调整到内核态的一次系统调用+内核层遍历文件描述符。
在这里插入图片描述

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

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

相关文章

什么是零知识证明?

零知识证明(Zero Knowledge Proof,以下简称ZKP)是一种加密学中的重要技术,它可以让一个人向另一个人证明某个事情是真的,而不需要透露这个事情的具体内容,即不需要泄露任何信息。ZKP 技术可以在不牺牲隐私的…

难见的oracle 9i恢复---2023年----惜分飞

时过境迁,以前恢复大量oracle 8/9版本的库,现在一套oracle 9i的库都比较稀奇了.今天恢复客户一套9.2.0.6的aix环境rac库,通过分析确认主要问题: 1. 重建控制文件,resetlogs库遗漏数据文件 2. 数据库启动主要报错ORA-600 2663和ORA-600 kclchkblk_4 Tue Nov 8 09:…

Python dshelper:动动鼠标,搞定数据探索!

本次分享一个Python数据探索小工具dshelper,适合快速查看数据基本特征、数据可视化等使用场景。 无需代码,自动完成数据集描述统计; 无需代码,界面点鼠标绘制多种统计图: 支持命令行、jupyter notebook、docker三种…

RK3588平台开发系列讲解(进程篇)Linux 进程的数据结构

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、Linux 进程的数据结构二、创建 task_struct 结构三、Linux 进程地址空间四、Linux 进程文件表沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 本篇将介绍 Linux 如何表示进程。 一、Linux 进程的数据结构…

Java测试:OJ练习(字符串合并后返回按照先后顺序排列的不重复新字符串、合并数组并按升序排列、Arrays 类中的sort方法)

1、给定一个长度为n的字符 串,字符串中只包含大小写字母。 请你返回该字符串拥有那些字符。 并将它们按照出现的先后!顺序拼接成一个新的字符串。 这是我最开始写的,代码有点问题: public String setString(String str) {char[]…

文本三剑客之——Awk

Awk Awk简介Awk语法格式Awk常见内置变量Awk实例演示按行输出文本BEGIN模式和END模式按字段输出文本通过管道,双引号调用shell命令date 的用法getline的用法awk数组 Awk简介 Awk是一个功能强大的编辑工具,用于在Linux/UNIX 下对文本和数据进行处理。数据…

代码随想录算法训练营第六天|242.有效的字母异位词 、349. 两个数组的交集 、202. 快乐数、1. 两数之和

哈希表的表示方式:数组、set、map 数组:范围可控的情况下,可以用数组 set:哈希值较大的情况下,或数值分布很分散的情况 map:key 和 value对应的情况下 能用数组尽量用数组,因为数组会比较快&…

Netty内存管理

关键概念 PoolArena——内存管理的统筹者 PoolArena是内存管理的统筹者。它内部有一个PoolChunkList组成的链表 PoolChunkList——对PoolChunk的管理 PoolChunkList内部有一个PoolChunk组成的链表。通常一个PoolChunkList中的所有PoolChunk使用率(已分配内存/ChunkSize)都在…

机器学习算法分类

机器学习常用算法的分类: 根据数据集组成不同,可以把机器学习算法分为: 监督学习无监督学习半监督学习强化学习 1、监督学习 - 定义: - 输入数据是由输入特征值和目标值所组成 - 函数的输出可以是一个连续的值(称为回…

【文本三剑客】AWK

AWK 一、AWK的工作原理1.1命令格式1.2awk常见的内建变量 二、awk实验2.1按行输入文本2.2按字段输出文本2.3通过管道符、双引号调用shell命令 一、AWK的工作原理 逐行读取文本,默认以空格或tab键为分隔符进行分隔,将分隔所得的各个字段保存到内建变量中&…

银行数字化转型导师坚鹏:银行数字化转型面临的5大机遇与4大挑战

在机遇方面,主要面临以下5大机遇: 国家战略及政策机遇:乡村振兴战略、制造强国战略、绿色金融战略等战略的落实将会给银行数字化转型带来新的业务机遇,《中国银保监会关于推动银行业和保险业高质量发展的指导意见》、《关于银行业…

第五章 面向对象-4abstract抽象

1.4 abstract class抽象类 声明抽象类&#xff0c;使用关键字abstract //内部匿名类 Db db new Db(){ };3.了解抽象类 抽象方法 AbstractClassMain.java /** Copyright (c) 2017, 2023, zxy.cn All rights reserved.**/ package cn.practice2;/*** <p>Description:&…

Chatgpt中文版无需代理,ChatGPT镜像

Chatgpt中文版无需代理 网站ChatGPT中文版 ChatGPT中文版是一个基于人工智能技术的聊天机器人&#xff0c;它可以模拟人类的自然语言交互&#xff0c;回答用户的各种问题和提供各种服务。它的核心技术是GPT&#xff08;Generative Pre-trained Transformer&#xff09;模型&am…

基础:Android相关基础知识

目录 1.Android 四大组件 2.Activity生命周期 3.Service的生命周期 4.Service的启动方式 5.Activity的启动模式 6.广播的分类 7.ANR是什么&#xff0c;怎么避免&#xff1f; 8.Handler消息处理机制 9.事件分发机制 10.View绘制流程 11.Binder机制 12.进程间通信 1…

2023最新一键开通主机免费源码

更新了ui 自助开通主机&#xff0c;自己修改服务器 不带接口&#xff0c;不带接口&#xff0c;不带接口 打开api.php文件&#xff0c;把8.8.8.8改服务器ip&#xff0c;123456改成你的密钥 前往我的技术博客查看更多https://202271.xyz/?zhuji 蓝奏云链接 https://wwp.lanz…

如何在Linux中显示网络连接、路由表、接口统计等信息?Netstat了解一下!

Netstat 是一个用于显示网络连接、路由表、接口统计等信息的命令行工具。它在 Linux 和其他类 Unix 系统中都有提供&#xff0c;可以帮助我们分析和诊断网络问题。本文将介绍 Netstat 命令的基本用法和常见选项。 Netstat 命令的语法 Netstat 命令的基本语法如下&#xff1a; …

rtl仿真器-ghdl安装和测试

安装 sudo add-apt-repository ppa:pgavin/ghdl sudo apt-get update sudo apt-get install ghdl gtkwave仿真 rtl add.v library ieee; use ieee.std_logic_1164.all; entity ADD is port (A,B:in bit; SUM,CARRY:out bit); end entity ADD; architecture behave of ADD i…

前端部署vue项目到腾讯云服务器

先把dist包上传服务器 可以使用宝塔、FileZilla、手动上传等等方式 已有腾讯云服务器之后进入面板界面 然后安装Nginx 请一步一步&#xff0c;紧跟步骤 第一步 安装gcc-c 编译器。nginx依赖的 pcre 和 zlib 包 yum -y install gcc zlib zlib-devel pcre-devel openssl openss…

嵌入式通信协议【Modbus】Modbus功能码的详细描述

一、读功能码 1、 01 (0x01)读线圈 在一个远程设备中&#xff0c;使用该功能码读取线圈的 1 至 2000 连续状态。请求 PDU 详细说明了起始地址&#xff0c;即指定的第一个线圈地址和线圈编号。从零开始寻址线圈。因此寻址线圈 1-16 为 0-15。 根据数据域的每个比特将响应报文…