Redis IO多路复用

news2024/11/17 2:41:20

0、前言

本文所有代码可见 => 【gitee code demo】
本文涉及的主题:

1、BIO、NIO的业务实践和缺陷

2、Redis IO多路复用:redis快的主要原因

3、epoll 架构

部分图片 via 【epoll 原理分析】

1、BIO单线程版

1.1 业务代码

client client代码相同 启动多个即可

public class RedisClient1 {
	
	public static void main(String[] args) throws IOException {
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
		Socket socket = new Socket("127.0.0.1", 6300);
		log("{} {}> 尝试连接服务 {}", sdf.format(new Date()) ,socket.getLocalPort(), socket.getPort());
		OutputStream outputStream = socket.getOutputStream();
		
		
		while (true) {
			Scanner scanner = new Scanner(System.in);
			log("{} {}> ", sdf.format(new Date()) ,socket.getLocalPort());
			String string = scanner.nextLine();
			if (string.equalsIgnoreCase("quit")) {
				break;
			}
			socket.getOutputStream().write(string.getBytes());
			log("{} {}> 发送数据:{}", sdf.format(new Date()) ,socket.getLocalPort(), string);
		}
		outputStream.close();
		socket.close();
	}
	
}

server

public class RedisServerBIO {
	public static void main(String[] args) throws IOException {
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
		
		ServerSocket serverSocket = new ServerSocket(6300);
		while (true) {
			log("{} {}> ", sdf.format(new Date()), serverSocket.getLocalPort());
			Socket socket = serverSocket.accept();//阻塞1 ,等待客户端连接
			log("{} {}> {} 连接到服务", sdf.format(new Date()), socket.getLocalPort(), socket.getPort());
			InputStream inputStream = socket.getInputStream();
			int length = -1;
			byte[] bytes = new byte[1024];
			log("{} {}> ", sdf.format(new Date()), serverSocket.getLocalPort());
			while ((length = inputStream.read(bytes)) != -1)//阻塞2 ,等待客户端发送数据
			{
				log("{} {}> 收到 {} 的消息:{}", sdf.format(new Date()), serverSocket.getLocalPort(), socket.getPort(), new String(bytes, 0, length));
			}
			inputStream.close();
			socket.close();
		}
	}
}

1.2 结果演示

现象:

1、client1 连接到server,client2尝试连接被阻塞

2、client2 先发送的消息未被server接受,client1后发送的repeat消息被server接受

结论:

BIO会一直阻塞,单线程下只能处理一个socket连接

存在的问题:

多 client 访问时效率低

在这里插入图片描述

2、BIO多线程版

2.1 业务代码

public class RedisServerBIOMultiThread {
	public static void main(String[] args) throws IOException {
		ServerSocket serverSocket = new ServerSocket(6300);
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
		log("{} {}> ", sdf.format(new Date()), serverSocket.getLocalPort());
		
		while (true) {
			Socket socket = serverSocket.accept();//阻塞1 ,等待客户端连接
			log("{} {}> {} 连接到服务", sdf.format(new Date()), socket.getLocalPort(), socket.getPort());
			
			new Thread(() -> {
				try {
					InputStream inputStream = socket.getInputStream();
					int length = -1;
					byte[] bytes = new byte[1024];
					while ((length = inputStream.read(bytes)) != -1)//阻塞2 ,等待客户端发送数据
					{
						log("{} {}> 收到 {} 的消息:{}", sdf.format(new Date()), serverSocket.getLocalPort(), socket.getPort(), new String(bytes, 0, length));
					}
					inputStream.close();
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}, Thread.currentThread().getName()).start();
			
			System.out.println(Thread.currentThread().getName());
			
		}
	}
}

2.1 结果演示

现象:

client1 、client2 都能正常连接到 server且正常发送、接受消息

结论:

BIO多线程提高处理能力,可以同时处理多个socket连接

存在的问题:

每个线程只能处理一个socket,当client数量大时,需要消耗大量线程资源

在这里插入图片描述

3、NIO

3.1 业务代码

当一个客户端与服务端进行连接,这个socket就会加入到一个容器中,隔一段时间遍历一次,看这个socket的read()方法能否读到数据,这样一个线程就能处理多个客户端的连接和读取了

public class RedisServerNIO {
	static ArrayList<SocketChannel> socketList = new ArrayList<>();
	static ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
	
	public static void main(String[] args) throws IOException {
		ServerSocketChannel serverSocket = ServerSocketChannel.open();
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
		serverSocket.bind(new InetSocketAddress("127.0.0.1", 6300));
		serverSocket.configureBlocking(false);//设置为非阻塞模式
			
		while (true) {
			for (SocketChannel element : socketList) {
				int read = element.read(byteBuffer);
				if (read > 0) {
					byteBuffer.flip();
					byte[] bytes = new byte[read];
					byteBuffer.get(bytes);
					//System.out.println(JSONUtil.toJsonStr(element));
					log("{} {}> 收到 {} 的消息:{}", sdf.format(new Date()), element.socket().getLocalPort(), element.socket().getPort(), new String(bytes, 0, read));
					byteBuffer.clear(); 
				}
			}
			
			SocketChannel socketChannel = serverSocket.accept();
			if (socketChannel != null) {
				log("{} {}> {} 连接到服务", sdf.format(new Date()), socketChannel.socket().getLocalPort(), socketChannel.socket().getPort());
				socketChannel.configureBlocking(false);//设置为非阻塞模式
				socketList.add(socketChannel);
				log("{} {}> socket 数量: {} ", sdf.format(new Date()), socketChannel.socket().getLocalPort(), socketList.size());
			}
		}
	}
}

3.2 结果演示

现象:

1、client1 、client2 都能正常连接到 server且正常发送、接受消息

2、server 没有创建额外线程

结论:

NIO 可以实现一个线程处理多个 socket 连接

存在的问题:

1、每次遍历所有socket,有很多无用功

2、遍历过程在用户态,还需要将数据从内核态读取到用户态

在这里插入图片描述

4、IO多路复用

1、使用 epoll() 实现,多个网络连接 socket 复用同一个线程

2、基于事件驱动机制,socket 中有数据会主动通知内核,并加入到就绪链表中,不需要遍历所有 socket

3、减少了内核态和用户态的切换

Redis IO多路复用实现

在这里插入图片描述

4.1 epoll_create()

创建内核中的fd容器

4.2 epoll_ctl()

epoll_ctl函数用于增加,删除,修改epoll事件,epoll事件会存储于内核epoll结构体红黑树中

4.3 epoll_wait

用于监听套接字事件,可以通过设置超时时间timeout来控制监听的行为为阻塞模式还是超时模式

4.4 epoll 软件架构

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

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

相关文章

Proxmox VE 8虚拟机直通USB磁盘

作者&#xff1a;田逸&#xff08;fromyz&#xff09; 今天有个兄弟发消息&#xff0c;咨询怎么让插在服务器上的U盾被Proxmox VE上的虚拟机识别。在很久很久以前&#xff0c;我尝试过在Proxmox VE 5以前的版本创建windows虚拟机&#xff0c;并把插在Proxmox VE宿主机上的银行U…

Vue3基础(二)

一、搭建工程(vite) ## 1.创建命令 npm create vuelatest## 2.具体配置 ## 配置项目名称 √ Project name: vue3_test ## 是否添加TypeScript支持 √ Add TypeScript? Yes ## 是否添加JSX支持 √ Add JSX Support? No ## 是否添加路由环境 √ Add Vue Router for Single P…

【matlab】智能优化算法——基准测试函数

智能优化算法的基准测试函数是用于评估和优化算法性能的一组标准问题。这些测试函数模拟了真实世界优化问题的不同方面&#xff0c;包括局部最小值、全局最优解、高维度、非线性、不连续等复杂性。以下是对智能优化算法基准测试函数的详细归纳&#xff1a; 测试函数的分类&…

使用nohup和CUDA_VISIBLE_DEVICES进行GPU训练的教程

文章目录 1. 在单个GPU上训练模型1.1 使用nohup命令运行Python脚本1.2 查看运行中的进程1.3 查看输出日志 2. 在多个GPU上训练模型2.1 启动第一个程序&#xff0c;指定使用第0号GPU2.2 启动第二个程序&#xff0c;指定使用第1号GPU2.3 查看运行中的进程2.4 查看输出日志 3. 总结…

【不容错过】可灵AI重磅更新:画质升级,运镜控制,首尾帧自定义,还有30万创作激励奖金!

还记得最近在各大平台肆虐的老照片变成视频吗&#xff0c;就是用快手的可灵AI做的&#xff0c;今天可灵又迎来了一次重大更新。 「电脑端上线了」 之前一直用其他工具生的图片还需要保存到手机上&#xff0c;再用可灵来生成视频&#xff0c;很多人都能感受到手机操作不太方便&…

【ARMv8/v9 GIC 系列 5.6 -- GIC 超优先级中断详细介绍】

请阅读【ARM GICv3/v4 实战学习 】 文章目录 Interrupt superpriority超优先级中断的特性和应用Physical interface interrupt signalsPhysical Group 1 Non-NMI for Current Security StatePhysical Group 1 for Other Security State, or a Group 0 Non-NMIPhysical Group 1 …

上海外贸建站公司wordpress模板推荐

Sora索啦高端制造业wordpress主题 红色高端制造业wordpress主题&#xff0c;适合外贸企业出海建独立站的wordpress模板。 https://www.jianzhanpress.com/?p5885 Yamal外贸独立站wordpress主题 绿色的亚马尔Yamal外贸独立站wordpress模板&#xff0c;适用于外贸公司建独立站…

Akamai+Noname强强联合 | API安全再加强

最近&#xff0c;Akamai正式完成了对Noname Security的收购。本文我们将向大家介绍&#xff0c;经过本次收购后&#xff0c;Akamai在保护API安全性方面的后续计划和未来愿景。 Noname Security是市场上领先的API安全供应商之一&#xff0c;此次收购将让Akamai能更好地满足日益增…

部署AI平台-Ollama

介绍 llama&#xff1a;LLaMA(Large Language Model Meta AI)是由 Meta(原Facebook公司)发布的一系列大型语言模型。这些模型旨在处理和生成自然语言文本&#xff0c;能够执行多种任务&#xff0c;如文本摘要、翻译、问答、文本生成等。LLaMA 模型因其高效的性能和较小的模型尺…

一行代码用git新建分支

1.在本地创建分支 dev git branch dev2.切换分支 git checkout devwebstorm操作如下&#xff1a; 3.推送新分支到远程 git push --set-upstream origin 分支名webstorm操作如下&#xff1a;提交代码的时候会自动推送到远程 4.到git上面可以看看刚刚推送的内容 dev多推送…

Linux应用---信号

写在前面&#xff1a;在前面的学习过程中&#xff0c;我们学习了进程间通信的管道以及内存映射的方式。这次我们介绍另外一种应用较为广泛的进程间通信的方式——信号。信号的内容比较多&#xff0c;是学习的重点&#xff0c;大家一定要认真学&#xff0c;多多思考。 一、信号概…

VBA初学:零件成本统计之二(材料外协金额表)

第二步&#xff0c;通过已经生成的机加任务&#xff0c;生成汇总表格及材料外协金额表 生成汇总统计和材料外协金额表 Sub statistical() Dim WS As Worksheet Dim rng As Range, rngold As Range Dim sheetName As String Dim rowscount As Long, MAXRGN As Long Dim i As Int…

237 删除链表中的节点

题目 有一个单链表的 head&#xff0c;我们想删除它其中的一个节点 node。 给你一个需要删除的节点 node 。你将 无法访问 第一个节点 head。 链表的所有值都是 唯一的&#xff0c;并且保证给定的节点 node 不是链表中的最后一个节点。 删除给定的节点。注意&#xff0c;删…

微信小程序简历Demo

微信小程序简历Demo 使用介绍最后获取源码 bilibili视频介绍 使用介绍 使用微信小程序实现的一个简历实现Demo 拖动马里奥&#xff0c;到指定Name下方 向上顶就可以显示对应的简历样式 点击头像可拨打电话 点击信息处可显示当前位置 最后 这是一个简单并且有趣的微信小程…

Nginx-http_limit_req_module模块

文章目录 前言一、ngx_http_limit_req_module模块二、指令1.limit_req_zone2.limit_req3.limit_req_log_level4.limit_req_status 实验burst取默认0的情况burst不取默认值 总结 前言 如何限制每个客户端每秒处理请求数 一、ngx_http_limit_req_module模块 生效阶段&#xff1…

Type-C接口快充取电技术的实现

Type-C接口快充取电技术的实现 Type-C接口快充取电技术主要通过USB PD&#xff08;Power Delivery&#xff09;协议实现。这种技术利用了Type-C接口的物理特性和PD协议的智能性&#xff0c;实现了高效、安全、快速的充电过程。具体实现过程如下&#xff1a; 接口连接与检测&a…

ctfshow-web入门-文件包含(web88、web116、web117)

目录 1、web88 2、web116 3、web117 1、web88 没有过滤冒号 : &#xff0c;可以使用 data 协议&#xff0c;但是过滤了括号和等号&#xff0c;因此需要编码绕过一下。 这里有点问题&#xff0c;我 (ls) 后加上分号发现不行&#xff0c;可能是编码结果有加号&#xff0c;题目…

vue3+vite搭建第一个cesium项目详细步骤及环境配置(附源码)

文章目录 1.创建vuevite项目2.安装 Cesium2.1 安装cesium2.2 安装vite-plugin-cesium插件&#xff08;非必选&#xff09;2.3 新建组件页面map.vue2.4 加载地图 3.完成效果图 1.创建vuevite项目 打开cmd窗口执行以下命令&#xff1a;cesium-vue-app是你的项目名称 npm create…

安全及应用(更新)

一、账号安全 1.1系统帐号清理 #查看/sbin/nologin结尾的文件并统计 [rootrootlocalhost ~]# grep /sbin/nologin$ /etc/passwd |wc -l 40#查看apache登录的shell [rootrootlocalhost ~]# grep apache /etc/passwd apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin#改变…

深度学习基础以及vgg16讲解

一 什么是卷积 上图所示&#xff0c;为图像边缘提取得一个卷积过程&#xff0c;卷积核就是计算当前像素左右两边得像素差&#xff0c;这个差值越大代表越可能是图像边缘。因此当实现其它功能时&#xff0c;只需要调整卷积核得参数即可。深度学习的训练其实就是在确定这些参数。…