Netty学习——BIO、NIO、AIO的总结

一、简介

Java中的BIO、BIO和AIO都可以理解为 Java对操作系统各种IO模型的封装。无需关心操作系统层面的知识,只需要使用Java的API就可以了.
首先需要了解一下几个基本概念:同步和异步,阻塞和非阻塞

同步和异步
  • 同步:同步是致发起一个调用后,被调用折不处理完请求,比返回
  • 异步:表示发起一个调用后,会立即收到被调用者的反馈,表示已经接受到信息来,但是并没有返回结果。此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。
阻塞和非阻塞
  • 阻塞: 阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。
  • 非阻塞: 非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。
二、BIO (Blocking I/O )

同步阻塞I/O模式,数据的读写必须阻塞在一个线程中等待其完成

传统的BIO (一请求一应答)模式图如下:
Netty学习——BIO、NIO、AIO的总结
对于BIO的这种服务模型,是通过由一个独立的Acceptor线程负责监听客户端的链接。请求一旦接收到一个连接请求,就可以建立通信套接字在这个通信套接字上进行读写操作,此时不能再接收其他客户端连接请求,只能等待同当前连接的客户端的操作执行完成, 不过可以通过多线程来支持多个客户端的连接。
如果BIO要能够同时处理多个客户端,则就需要多线程来完成。主要是因为socket.accept(),socket.read()和socket.writer都是同步阻塞的方法。也就是它收到一个客户端请求后,就需要创建一个新的线程来进行链路处理,处理完成之后,应答客户端,线程销毁。是一个典型的 一请求一答应的通行模型,不过这个方式会操作线程大量不必要的开销。所以我们可以通过线程池机制来改善,让线程的创建和回收成本相当降低。具体的伪异步IO 可以详见 github中的IO文档

BIO 总结

在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。

三、NIO (New I/O No-Blocking I/O)

NIO是一直同步非阻塞I/O模型,在Java1.4中引入的。并提高了 Channel、Buffer 和Selector等抽象类。它支持面向缓存的,基于通道的I/O操作方法。支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发。

NIO/IO的区别
  • IO流是阻塞性的,NIO是非阻塞的
    Java NIO使我们可以进行非阻塞IO操作。比如说,单线程中从通道读取数据到buffer,同时可以继续做别的事情,当数据读取到buffer中后,线程再继续处理数据。写数据也是一样的。另外,非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。

    Java IO的各种流是阻塞的。这意味着,当一个线程调用 read() 或 write() 时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了

  • ** IO 面向流(Stream),而 NIO 面向缓冲区(Buffer)**
    在IO中,是可以将数据直接写入或者将数据直接读到 Stream 对象中
    在NIO库中,所有数据都是用缓冲区来处理的。在读取数据时,它是直接读到缓冲区中的; 在写入数据时,写入到缓冲区中。任何时候访问NIO中的数据,都是通过缓冲区进行操作。
    最常用的缓冲区是 ByteBuffer,一个 ByteBuffer 提供了一组功能用于操作 byte 数组。除了ByteBuffer,还有其他的一些缓冲区,事实上,每一种Java基本类型(除了Boolean类型)都对应有一种缓冲区。

  • NIO有通道 Channel
    通道是双向的,可读也可写,而流的读写是单向的。无论读写,通道只能和Buffer交互。因为 Buffer,通道可以异步地读写。

  • NIO有选择器 Selector
    选择器用于使用单个线程处理多个通道。因此,它需要较少的线程来处理这些通道。线程之间的切换对于操作系统来说是昂贵的。 因此,为了提高系统效率选择器是有用的。
    Netty学习——BIO、NIO、AIO的总结

  • NIO 读数据和写数据方式
    通常来说NIO中的所有IO都是从 Channel(通道) 开始的。
    从通道进行数据读取 :创建一个缓冲区,然后请求通道读取数据。
    从通道进行数据写入 :创建一个缓冲区,填充数据,并要求通道写入数据。

  • NIO核心组件简单介绍
    NIO 包含下面几个核心的组件:
    Channel(通道)
    Buffer(缓冲区)
    Selector(选择器)
    整个NIO体系包含的类远远不止这三个,只能说这三个是NIO体系的“核心API”。我们上面已经对这三个概念进行了基本的阐述,这里就不多做解释了。

  • NIO的缺点

    1. JDK 的 NIO 底层由 epoll 实现,该实现饱受诟病的空轮询 bug 会导致 cpu 飙升 100%
    2. API复杂,自行实现的 NIO 很容易出现各类 bug,维护成本较高,上面这一坨代码我都不能保证没有 bug
    3. Netty 的出现很大程度上改善了 JDK 原生 NIO 所存在的一些让人难以忍受的问题。
四、AIO (Asynchronous I/O)

AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。

AIO 是异步IO的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。(除了 AIO 其他的 IO 类型都是同步的,这一点可以从底层IO线程模型解释,推荐一篇文章:《漫话:如何给女朋友解释什么是Linux的五种IO模型?》 )

参考 (https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/BIO-NIO-AIO.md)[https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/BIO-NIO-AIO.md]