Netty基础(一)NIO

101 阅读6分钟

资料来源:黑马netty

一、简介

Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的IO操作。

Java NIO系统的核心在于:通道(Channel)和缓冲区(Buffer)。通道表示打开到 IO 设备(例如:文件、套接字)的连接。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理。简而言之,通道负责传输,缓冲区负责存储。

image.png

image.png

image.png

二、通道(Channel),Buffer( 缓冲)和selector (选择器)

1. Channel

channel 有一点类似于 stream,它就是读写数据的双向通道,可以从 channel 将数据读入 buffer,也可以将 buffer 的数据写入 channel,而之前的 stream 要么是输入,要么是输出,channel 比 stream 更为底层。

常见的 Channel 有

  • 本地文件IO:FileChannel
  • 网络IO:SocketChanel、ServerSocketChannel:用于TCP传输
  • DatagramChannel:用于UDP传

image.png

image.png

image.png

2. Buffer

buffer 则用来缓冲读写数据,常见的 buffer 有

  • ByteBuffer
    • MappedByteBuffer
    • DirectByteBuffer
    • HeapByteBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer
  • CharBuffer

3. Selector

(1) 多线程版设计

image.png

(2) 线程池版设计

image.png

(3) Selector 设计

image.png

三、ByteBuffer

1. ByteBuffer正确使用姿势

  • 向 buffer 写入数据,例如调用 channel.read(buffer)
  • 调用 flip() 切换至读模式
  • 从 buffer 读取数据,例如调用 buffer.get()
  • 调用 clear() 或 compact() 切换至写模式
  • 重复 1~4 步骤

2. ByteBuffer结构

image.png

image.png

image.png

3. ByteBuffer常用方法

image.png

image.png

image.png

4. Scattering Reads

image.png

5. Gathering Reads

image.png

四、文件编程

1. FileChannel

  • FileChannel 只能工作在阻塞模式下
  • 获取:不能直接打开 FileChannel,必须通过 FileInputStream、FileOutputStream 或者 RandomAccessFile 来获取 FileChannel,它们都有 getChannel 方法
  • 读取:会从 channel 读取数据填充 ByteBuffer,返回值表示读到了多少字节,-1 表示到达了文件的末尾
  • 写入
  • 关闭:channel 必须关闭,不过调用了 FileInputStream、FileOutputStream 或者 RandomAccessFile 的 close 方法会间接地调用 channel 的 close 方法
  • 位置
  • 大小
  • 强制写入

2. 两个Channel传输数据

3. Path

l jdk7 引入了 Path 和 Paths 类: Path 用来表示文件路径;Paths 是工具类,用来获取 Path 实例

4. Files

五、网络编程

1. 非阻塞和阻塞和多路复用

image.png

image.png

image.png

单线程可以配合Selector完成对多个Channel可读写事件的监控,这称之为多路复用。它在非阻塞的基础上加了事件概念,只有事件发生了才会让线程继续运行,如果没有事件发生就是阻塞状态。****

2. Selector

image.png

image.png

image.png

image.png

3. 处理accept事件

事件发生后能否不处理

事件发生后,要么处理,要么取消(cancel),不能什么都不做,否则下次该事件仍会触发,这是因为 nio 底层使用的是水平触发

4. 处理read 事件

(1)为何要 iter.remove()

因为 select 在事件发生后,就会将相关的 key 放入 selectedKeys 集合,但不会在处理完后从 selectedKeys 集合中移除,需要我们自己编码删除。例如

  • 第一次触发了 ssckey 上的 accept 事件,没有移除 ssckey
  • 第二次触发了 sckey 上的 read 事件,但这时 selectedKeys 中还有上次的 ssckey ,在处理时因为没有真正的 serverSocket 连上了,就会导致空指针异常

(2) cancel 的作用

cancel 会取消注册在 selector 上的 channel,并从 keys 集合中删除 key 后续不会再监听事件

(3) 边界问题

image.png

image.png

(4) ByteBuffer 大小分配

每个 channel 都需要记录可能被切分的消息,因为 ByteBuffer 不能被多个 channel 共同使用,因此需要为每个 channel 维护一个独立的 ByteBuffer

ByteBuffer 不能太大,比如一个 ByteBuffer 1Mb 的话,要支持百万连接就要 1Tb 内存,因此需要设计大小可变的 ByteBuffer

  • 一种思路是首先分配一个较小的 buffer,例如 4k,如果发现数据不够,再分配 8k 的 buffer,将 4k buffer 内容拷贝至 8k buffer,优点是消息连续容易处理,缺点是数据拷贝耗费性能,参考实现 tutorials.jenkov.com/java-perfor…

  • 另一种思路是用多个数组组成 buffer,一个数组不够,把多出来的内容写入新的数组,与前面的区别是消息存储不连续解析复杂,优点是避免了拷贝引起的性能损耗

5. 处理 write 事件

image.png

6. 其他

前面的代码只有一个选择器,没有充分利用多核 cpu,如何改进呢?

分两组选择器

  • 单线程配一个选择器,专门处理 accept 事件

  • 创建 cpu 核心数的线程,每个线程配一个选择器,轮流处理 read 事件

六、NIO 和BIO 和AIO

1. 定义

  • BIO 就是传统的 java.io 包,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用时可靠的线性顺序。它的有点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。
  • NIO 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式
  • AIO 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以人们叫它 AIO(Asynchronous IO),异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。

2. Stream 和channel

  • stream 不会自动缓冲数据,channel 会利用系统提供的发送缓冲区、接收缓冲区(更为底层)
  • stream 仅支持阻塞 API,channel 同时支持阻塞、非阻塞 API,网络 channel 可配合 selector 实现多路复用
  • 二者均为全双工,即读写可以同时进行

3. IO 模型

  • 同步阻塞、同步非阻塞、同步多路复用、异步阻塞(没有此情况)、异步非阻塞
  • 同步:线程自己去获取结果(一个线程);异步:线程自己不去获取结果,而是由其它线程送结果(至少两个线程)

  image.png

image.png

image.png

image.png

image.png

 

4. 零拷贝

零拷贝的“零”是指用户态和内核态间 copy 数据的次数为零。

传统的数据 copy(文件到文件、client 到 server 等)涉及到四次用户态内核态切换、四次 copy。四次 copy 中,两次在用户态和内核态间 copy 需要 CPU 参与、两次在内核态与 IO 设备间 copy 为 DMA 方式不需要 CPU 参与。零拷贝避免了用户态和内核态间的 copy、减少了两次用户态内核态间的切换。

零拷贝可以提高数据传输效率,但对于需要在用户传输过程中对数据进行加工的场景(如加密)就不适合使用零拷贝。

image.png

image.png

参考:jishuin.proginn.com/p/763bfbd66…

参考:www.cnblogs.com/yc3110/p/10…

参考:xie.infoq.cn/article/e03…

image.png

image.png

image.png

image.png

5. AIO相关

image.png