Netty - NIO基础学习

一 简介

1 三大模型是什么?

IO三大模型之一,BIO,AIO,还有我们的主角NIO(non-blocking-io),也就是同步非阻塞式IO。这三种模型到底是干什么的?其实这三种模型都是对于JAVA的一种I/O框架,用来进行输入,输出操作。另一方面,以上三种模型提供的是统一的API操作,可以理解为JAVA针对于各种操作系统的IO模型进行的封装,从而具有一定的跨平台性等。作为开发者,我们不需要再了解有关操作系统层面的知识,直接使用这三种框架封装的API进行使用即可!

2 NIO

NIO(New Input/Output)是Java中的一种I/O(输入/输出)框架,主要用于处理异步I/O操作。它在Java 1.4版本中引入,旨在提高I/O操作的性能和可扩展性。

NIO的主要特点:

  1. 非阻塞I/O:NIO允许线程在发起I/O操作后继续执行其他任务,而不必等待I/O操作完成,从而提高了应用的响应性。

  2. 选择器(Selector):NIO引入了选择器,允许单个线程管理多个通道(Channel),从而减少了资源消耗和上下文切换的开销。

  3. 通道(Channel):NIO使用通道进行数据的读写,通道是双向的,可以同时进行读和写操作。

  4. 缓冲区(Buffer):NIO使用缓冲区来存储数据,缓冲区在读写过程中充当数据的临时存储区。

  5. 文件通道(FileChannel):NIO还提供了文件通道,可以用于高效地处理文件I/O。

NIO特别适合于需要处理大量并发连接的网络应用,如Web服务器和聊天应用等。通过非阻塞I/O和选择器,NIO能够更好地利用系统资源,提高整体性能。

二 NIO三大组件

1.Channel(通道)

相当于我们之前学习IO流操作中的输入输出流,起到一个通道的作用。但是在这里这个更加全面,既可以充当输入流,又可以充当输出流。是为双向通道。channel比Stream更底层,双向。

2.Buffer(缓冲区)

缓冲区,作为一个在网络或者是本地文件与计算机之间的桥梁,任何数据在进入通道Channel之前都需要经过Buffer。前者可以将数据读入Buffer,也可以将Buffer当中的数据写入Channel当中

3.Selector(选择器)

选择器是NIO中用于处理非阻塞I/O的核心组件。它允许单个线程管理多个通道。

我认为将其与各种服务器设计模式相结合进行介绍,会更加清晰。

我们以BIO(同步阻塞IO)举例,这样更加方便理解

我们都知道,BIO是一种同步阻塞IO,在服务端与客户端之间,其会建立一个一个通道(Socket)以进行彼此之间的数据输入和输出,但是对于一个服务端,如果想要与多个客户端之间建立连接,那么就需要建立多个线程,也就是一个线程对应一个客户端。如下图:

但是这样存在很大的弊端,如果有很多的客户端都进行访问,那么就会创建多个线程与其连接 ,就会导致CPU资源被占用许多,甚至直接宕机,导致客户端无法被外部访问。各个线程上下文之间的来回切换,线程创建,也会消耗大量资源。(这也就是为什么要创建线程池了。。。。)另一方面,BIO还有一个特点,那就是阻塞 !如果一个客户端没有发送消息,那么对应的线程就会一直等待,直到客户端发送新的消息,才会继续进行执行。在此之前,等待的过程,就会造成CPU资源的大量占用。所以这种方式只适用于比较少的客户端访问。其实这就是服务器的设计之一 ----- 多线程设计模式

有的大佬就会想到,可以创建线程池来处理问题,没错,我们上面面对的一个大问题就是:线程会随着客户端的访问而不断的创建。从而导致资源的大量占用。使用线程池可以设定线程的最大数量,用以解决客户端访问过多造成的问题。但是这种解决方式也存在很大问题,至于为什么,大家可以想一想。

**线程池设计模式。**顾名思义,就是针对之前一个线程对应一个客户端的情形进行该进,变为一个线程针对多个客户端进行处理,如下:

这种设计模式相比多线程有所提高,但是因为在阻塞模式下,依旧是一个线程在处理时仅仅只能够处理一个客户端,也就是Socket连接,因此其适合一个线程下连接的Scoket比较少的情况。

Slector版设计模式。 selector 的作用就是配合一个线程来管理多个 channel ,获取这些 channel 上发生的事件,这些 channel 工作在非阻塞模式下不会让线程吊死在一个 channel 上。适合连接数特别多,但流量低的场景(low traffic),如下图:

这种设计模式,一个线程对应一个选择器,这个选择器会直接监视其下的所有通道,一旦这些通道有什么动作发生,选择器就会将其汇报给线程,由线程进行解决。

非阻塞的形式,也就是说,我们选择器监视下的各个通道,比如第一个通道,如果其当前没有任务,那么我们的选择器就不会等待,而是直接看下一个通道是否有任务。

适合流量比较低,指的就是,如果我们的通道一下接收一个比较大的数据,那么选择器就会长时间的停留于它,直到它的任务结束,数据如果比较大,也就是如果流量比较大,那么就会影响其他通道的正常使用,因此适用于流量比较低的场景。

由此可见,选择器适合于事件驱动的应用程序,可以有效地处理大量并发连接,减少线程上下文切换的开销。

三 Buffer - ByteBuffer

上面我们简单说了几个比较缓冲区的Buffer,但是比较常用的实际上就一个ByteBuffer,所以我们拿这个作为详细介绍

1.基本使用

java 复制代码
@Slf4j
public class ChannelDemo1 {
    public static void main(String[] args) {
        try (RandomAccessFile file = new RandomAccessFile("helloword/data.txt", "rw")) {
            FileChannel channel = file.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(10);
            do {
                // 向 buffer 写入
                int len = channel.read(buffer);
                log.debug("读到字节数:{}", len);
                if (len == -1) {
                    break;
                }
                // 切换 buffer 读模式
                buffer.flip();
                while(buffer.hasRemaining()) {
                    log.debug("{}", (char)buffer.get());
                }
                // 切换 buffer 写模式
                buffer.clear();
            } while (true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

以上使用通道FileChannel与Buffer进行了一个简单的文件读取,以及对应文件内容的日志打印。

2.BetyBuffer使用说明:

  1. 向 buffer 写入数据,例如调用 channel.read(buffer)

  2. 调用 flip() 切换至读模式

  3. 从 buffer 读取数据,例如调用 buffer.get()

  4. 调用 clear()compact() 切换至写模式

  5. 重复 1~4 步骤

3.ByteBuffer结构

其内部实际上是一个类似数组的存储结构,数组的长度也就是我们在使用allocate方法的时候自定义的。

ByteBuffer主要有三个比较重要的属性:

position(当前位置)

limit(限制)

capaciy(为数组容量)

刚开始写入数据操作

在我们刚开始进行数据写入操作的时候,position从头开始,后两个都在最后,每写入一个数据,对应的position指针就会向后移动一位,如图。

获取数据

使用filp获取数据的时候,对应的position指针就会从头开始,索引归零,取而代之的是limit,用来代替之前写入的数据的最大索引位置,为了防止读取出来的数据为空。

**clear动作发生之后,**会将数组内部的所有元素都清空,变为空,从头来过,重新进行写操作。

还有一种写操作,为**compact动作,**比如我们因为某种原因,之前没有读取完的数据,那么就会直接将其进向前压缩,并且更新对应读取的时候的Position以及Limit的位置。从而达到写的操作。

相关推荐
Eugene__Chen3 天前
java IO/NIO/AIO
java·python·nio
一个儒雅随和的男子3 天前
Netty前置基础知识之BIO、NIO以及AIO理论详细解析和实战案例
nio
Craaaayon4 天前
JVM虚拟机--JVM的组成
java·jvm·nio
森叶7 天前
Java NIO & Java 虚拟线程(微线程)与 Go 协程的运行原理不同 为何Go 能在低配机器上承接10万 Websocket 协议连接
java·websocket·nio
码熔burning7 天前
【Netty篇】Channel 详解
netty·nio·channel
码熔burning8 天前
【NIO番外篇】之组件 Selector
java·io·nio·selector
码熔burning11 天前
【NIO番外篇】之组件 Channel
java·nio·channel
zhangpeng45554794012 天前
用Java NIO模拟HTTPS
java·https·nio
蜗牛、Z1 个月前
Java NIO之FileChannel 详解
java·nio
嘉友1 个月前
NIO ByteBuffer 总结
java·后端·nio