BIOvsNIO in Java

IO原理BIO和NIO应用对比数据库和web-server的区别:

传统的IO操作(BIO)原理数据库:BIO+用户态直接IO+连接池

NIO三大核心概念:Channel, Buffer, Selector零拷贝Netty中的零拷贝常见零拷贝技术示意图用户态直接IO: 如MySQLmmap+writesendfilesendfile + DMA gather copy

IO原理

IO,即 input & output,输入和输出是指对内存的写入和读取,常见的IO操作有网络IO,磁盘IO,分别对应内存和网络、磁盘的交互,举例来说,从内存写入到磁盘为output,从磁盘中读取数据到内存为input。input和output涉及系统资源的调度,在内核态实现,而用户的调用处于用户态,因此存在用户态和内核态的切换。用户需要将数据从内核态拷贝到用户态,这种在内存中数据拷贝可以通过零拷贝技术避免。

BIO和NIO应用对比

本质上是因为不同的技术其实没有优劣之分,只是分别适用于不同的业务场景。

数据库和web-server的区别:

数据库只需要服务于编写的几个服务程序,只有几个连接,而且几个连接会经常使用,所以使用BIO且限制最大连接数的连接池就可以;web服务器则是被网络中无数的客户,并且网络传输为 请求-响应 模式,许多用户和服务端只有简单的交互,所以使用多路复用更加合理。 数据库的一个连接中的执行必须是串行、同步、有序的,Web-server则是无状态的,短促的,多个连接的执行顺序可以变化。 正是因为数据库使用BIO,一个线程负责一个连接,所以必须设置最大连接数,超出连接数时只能丢弃。

传统的IO操作(BIO)

原理

通常我们的研究重点是服务端,服务端传统的IO操作,比如读取磁盘中的文件,再通过网卡发送给客户端。用户发起读取请求后,需要切换到内核态读取磁盘文件,写入内核缓存,再切换到用户态,写入用户缓存;发送过程同理,用户发起,切换到内核态,将数据复制到内核缓存,进行发送。总共涉及2次CPU拷贝,2次DMA拷贝共四次拷贝,以及四次上下文切换。 编写代码时直接编写两个socket作为TCP服务端和客户端即可。

数据库:BIO+用户态直接IO+连接池

如前文所述,数据库采用BIO这种IO方式主要是考虑到数据库服务的场景,数据库为了避免用户态和内核态之间的重复拷贝,采用了用户态直接IO的方式,运行在用户态的应用程序使用用户态的库函数直接访问硬件设备,用户程序自己实现数据缓存,即bufferPool。

NIO

三大核心概念:Channel, Buffer, Selector

Channel 通道,从外设到内存的通路,外设包括各种数据源,如磁盘、网卡,或者说文件、套接字。TCP通信通道为SocketChannel和ServerSocketChannel。在BIO中,Socket与ServerSocket的关系为:ServerSocket是做服务端的,每次收到客户端Socket的TCP连接时,都会为该TCP连接生成一个Socket实例,记录了网络通信的基本信息并实现了相关基本操作。在NIO中的SocketChannel和ServerSocketChannel同理,只不过Socket与ServerSocket只能是阻塞的,而SocketChannel和ServerSocketChannel可以设置阻塞和非阻塞两种。

Buffer 缓冲区,读写直接操作的内存区域。输入Input是从Channel到buffer,输出是从buffer到channel,而传统的BIO中,读写都是针对流的,分为字符流和字节流。有容量、界限、位置、标记等几个成员变量,具有读、写两种模式,两种模式下成员变量的含义不同,如读模式下界限limit标识最大可以读的位置,写模式下则是最多可以写入的数据,位置position在进行读写模式切换后会被清零。

Selector 选择器,多路复用的核心,一个Selector监控所有注册的Channel,当channel中有需要处理IO事件时,会将相应的selectionKey加入到SelectedKeys集合中,在每一轮循环中会对集合中的事件依次处理。

零拷贝

Netty中的零拷贝

Netty是Java对NIO的封装,进行了一些优化和拓展,如增加了对SSL/TLS协议的支持。Netty中的零拷贝是一种用户进程级别的零拷贝体现,主要也包含三方面: 1) Netty的发送、接收数据的ByteBuf缓冲区,默认会使用堆外本地内存创建,采用直接内存进行Socket读写,数据传输时无需经过二次拷贝。如果使用传统的堆内存进行Socket网络数据读写,JVM需要先将堆内存中的数据拷贝一份到直接内存,然后才写入Socket缓冲区中,相较于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。 2)Netty的文件传输采用了**transferTo()/transferFrom()**方法,它可以直接将文件缓冲区的数据发送到目标Channel(Socket),底层就是调用了sendfile()内核函数,避免了文件数据的CPU拷贝过程。 3)Netty提供了组合、拆解ByteBuf对象的API,咱们可以基于一个ByteBuf对象,对数据进行拆解,也可以基于多个ByteBuf对象进行数据合并,这个过程中不会出现数据拷贝,这个是程序级别的零拷贝,实际上就是在原数据的基础上用不同的引用表示而已。

常见零拷贝技术示意图

用户态直接IO: 如MySQL

mmap+write

sendfile

sendfile + DMA gather copy

参考资料:

https://blog.csdn.net/qq_44969643/article/details/125109653https://blog.csdn.net/J080624/article/details/84238619https://zhuanlan.zhihu.com/p/83398714

推荐链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: