Netty 学习之 NIO / BIO / AIO 基础知识 (二)

Administrator
发布于 2019-05-12 / 689 阅读
0
0

Netty 学习之 NIO / BIO / AIO 基础知识 (二)

NIO

好吧,NIO 就是 new IO, 是java对原有IO流API的替代API,属于非阻塞型IO。 对于NIO,需要知道几个概念,Channel,Buffer,非阻塞,Selectors:

  • 标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。 即相关的操作,是针对Channel与与Buffer的。
  • 另一方面,Java NIO可以让你非阻塞的使用IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似。
  • Java NIO引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道。

Buffer

使用Buffer读写数据一般遵循以下四个步骤:

  1. 写入数据到Buffer
  2. 调用flip()方法
  3. 从Buffer中读取数据
  4. 调用clear()方法或者compact()方法

BIO

可以理解为block-io,即阻塞型IO,就是当你调用read时候,只有有数据才会返回,而没有数据时,是不会返回只会阻塞等待的。 传统的Java Socket就是这样一种机制,有数据才会返回,否则会一直阻塞。

比如我们使用Socket进行与服务端通信,默认情况下服务端需要对每个请求建立一个线程等待请求,而客户端发送请求后,先咨询服务端是否有线程响应,如果没有则会一直等待或者遭到拒绝请求,如果有的话,客户端的线程会等待请求结束得到服务端返回信息后才继续执行。

AIO

AIO在java1.7之后新加入进来的,可以理解为nio2.0,异步非阻塞型,重要的有以下几个类

AsynchronousSocketChannel
AsynchronousServerSocketChannel
AsynchronousFileChannel
AsynchronousByteChannel

它的伸缩性则更强了,在开始的NIO中,虽然是非阻塞的,但是还是需要轮训才知道那个通道数据可用。 而在AIO,并不需要selector去轮训了。 在AIO中,调用方法会立即返回,并且会告诉调用者,这次请求已经开始了,系统会使用另外的资源或者线程来完成 这次调用操作,并且在完成时候会通知调用者(比如回调方法)。

总结

从实现来看,传统IO与NIO确实存在不小的区别。 1、传统IO是一次读取4(或其他,有规律)个字节,而NIO,则是一次读取多个,直接存入buffer(在c++层面就是读取多个,而不是一次读取一个再在Java层面拼凑出Buffer)。 2、NIO在读取时,如果传入的不是directBuffer,则最终读取时,还是通过一个临时directBuffer读取,最后放入传入的buffer中。 3、传统IO读取经过以下2个步骤:

  • read()方法执行,表示服务器要去磁盘上读文件了,这会导致一个 sys_read()的系统调用。此时由用户态切换到内核态,完成的动作是:DMA把磁盘上的数据读入到内核缓冲区中(这也是第一次拷贝)。
  • read()方法的返回(这也说明read()是一个阻塞调用),表示数据已经成功从磁盘上读到内核缓冲区了。此时,由内核态返回到用户态,完成的动作是:将内核缓冲区中的数据拷贝到用户缓冲区(这是第二次拷贝)。 每一次传统io的操作,都伴随着由内核缓冲区到用户缓冲区的拷贝,以及用户状态切换。 而nio则可以一次传递多个字节到buffer中。 4、实现思路,传统IO是阻塞式的,而NIO则是非阻塞式的。 5、NIO中使用的directbuffer(普通buffer也是通过directbuffer来操作的): 内存映射IO:就是复用一个以上的虚拟地址可以指向同一个物理内存地址。将内核空间的缓冲区地址(内核地址空间)映射到物理内存地址区域,将用户空间的缓冲区地址(用户地址空间)也映射到相同的物理内存地址区域。从而数据不需要从内核缓冲区映射的物理内存地址移动到用户缓冲区映射的物理内存地址了。

NIO和AIO区别

二者在操作系统层面对文件的操作是一致的,均是以buffer为单位传送,而在通信模型上有区别: NIO通常采用Reactor模式,AIO通常采用Proactor模式。AIO简化了程序的编写,stream的读取和写入都有OS来完成,不需 要像NIO那样子遍历Selector。Windows基于IOCP实现AIO,Linux只有eppoll模拟实现了AIO。Java7之前的JDK只支持NIO和BIO,从7开始支持AIO。

关于Reactor模式和Proactor模式的一些参考

https://www.cnblogs.com/me115/p/4452801.html https://www.jianshu.com/p/b7723c489e1c 个人认为 Proactor模式的处理效率在某种情况下要快一些,毕竟是牺牲了更多的资源。


评论