Redis IO多路复用

news/2024/10/6 17:43:51/

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.ppmy.cn/news/1474007.html

相关文章

数据库——数据库性能优化

title: 数据库——数据库性能优化 date: 2024-07-06 12:26:21 tags: 数据库 categories: 数据库 cover: /image/T1.jpg description: 数据库——MySQL的性能优化 数据库优化 系统的吞吐量瓶颈往往出现在数据库的 访问速度 上&#xff0c;随着应用程序的运行&#xff0c;数据库…

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

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

(九)绘制彩色三角形

前面的学习中并未涉及到颜色&#xff0c;现在打算写一个例子&#xff0c;在顶点着色器和片元着色器中加入颜色&#xff0c;绘制有颜色的三角形。 #include <glad/glad.h>//glad必须在glfw头文件之前包含 #include <GLFW/glfw3.h> #include <iostream>void …

Spring框架Mvc(2)

1.传递数组 代码示例 结果 2.集合参数存储并进行存储类似集合类 代码示例 postman进行测试 &#xff0c;测试结果 3.用Json来对其进行数据的传递 &#xff08;1&#xff09;Json是一个经常使用的用来表示对象的字符串 &#xff08;2&#xff09;Json字符串在字符串和对象…

计算机专业怎么选择电脑

现在高考录取结果基本已经全部出来了&#xff0c;很多同学都如愿以偿的进入到了计算机类专业&#xff0c;现在大部分同学都在为自己的大学生活做准备了&#xff0c;其中第一件事就是买电脑&#xff0c;那计算机类专业该怎么选择电脑呢&#xff1f; 计算机专业是个一类学科&…

麒麟信安与湖南第一师范学院签署校企合作协议,共启产教融合新篇章

7月5日&#xff0c;麒麟信安、湖南欧拉生态创新中心与湖南第一师范学院校企合作协议签约仪式在长沙麒麟科技园举行。湖南第一师范学院校党委委员、副校长周发明&#xff0c;与麒麟信安副总裁王攀签署校企合作协议&#xff0c;并共同为“国产操作系统产学研基地”揭牌。计算机学…

6、Redis系统-数据结构-07-QuickList

七、快速列表&#xff08;QuickList&#xff09; 快速列表&#xff08;QuickList&#xff09;是 Redis 中用于实现列表&#xff08;List&#xff09;类型的一种高效数据结构。它结合了双向链表和压缩列表的优点&#xff0c;既支持高效的顺序访问&#xff0c;又能有效节省内存。…

Spring Boot 创建定时任务

在现代应用程序开发中&#xff0c;定时任务是一个常见的需求。Spring Boot作为一个强大的框架&#xff0c;提供了简单易用的定时任务调度功能。本文将详细介绍如何在Spring Boot中创建和管理定时任务&#xff0c;并提供完整的代码示例。 1. 什么是定时任务 定时任务是指在预定…