九 网络通信优化之I|O模型:如何解决高并发下I|O瓶颈?

162 阅读3分钟

大家好,我是小水珠。

提到JavaI/O,相信你一定不陌生。你可能使用I/O操作读写文件,也可能使用它实现Socket的信息传播...这些都是我们在系统中最常遇到的和I/O有关的操作。 今天,我们就来深入了解下JavaI/O在高并发,大数据业务场景下暴露出的性能问题,从源头入手,学习优化方法

一 什么是I/O

I/O是机器获取和交换信息的主要通道,而流是完成I/O操作的主要方式。

Java的I/O操作类在包java.io下,其中InputStream,OutputStream以及Reader,Writer类是I/O包中的4个基本类,他们分别处理字节流和字符流。如下图所示:

IO流.jpg

1.字节流

字节流 (2).jpg

2.字符流

字符流.jpg

二 传统I/O的性能问题

1.多次内存复制

多次内存复制 (2).jpg

2.阻塞

三 如何优化I/O操作

1.使用缓冲区优化读写流操作

2.使用DirectBuffer减少内存复制

减少内存复制.jpg

你肯定会想,为什么Java需要通过一个临时的非堆内存来复制数据呢?如果单纯使用Java堆内存进行数据拷贝,当拷贝的数据量比较大的情况下,Java堆的GC压力会比较大,而使用非堆内存可以降低GC压力。

DirectBuffer则是直接将步骤简化为数据直接保存到非堆内存,从而减少了一次数据拷贝。以下是JDK调用IOUtil.java类中的write方法:

微信图片_20220802231333.jpg

3.避免阻塞。优化I/O操作

NIO很多人也称之为非阻塞IO。因为这样叫,更能体现它的特点,为什么这么说呢?

通道(Channel)

Channel有自己的处理器,可以完成内核空间和磁盘之间的I/O操作。在NIO中,我们读取和写入数据都需要通过Channel,由于Channel是双向的,所以读,写可以同时进行。

多路复用器(Selector)

Selector是Java NIO编程的基础。用于检查一个或多个NIO Channel的状态是否处于可读,可写。

目前操作系统的I/O多路复用器机制都使用了epoll,相比传统的select机制,epoll没有最大连接句柄1024的限制。所以Selector在理论上可以轮训成千上万的客户端。

四 总结

Java的传统I/O最开始是基于InputStream和OutputStream这两个操作实现的,这种流操作是以字节为单位,如果在高并发,大数据场景中,很容易导致阻塞,因此这种操作的性能是非常差的。还有,输出数据从用户空间复制到内存空间,在复制到输出设备,这样的操作会增加系统的性能开销。

传统I/O后来使用了Buffer优化“阻塞”这个性能问题,以缓冲块作为最小单位,但相比整体性能来说依然不尽人意。

于是NIO发布,他是基于缓冲块为单位的流操作,在Buffer的基础上,新增了两个组件“管道和多路复用器”,实现了非阻塞I/O,实现了非阻塞I/O,NIO适用于发生大量I/O连接请求的场景,这三个组件共同提升I/O的整体性能。

完整内容,请关注

微信图片_20220724153846.jpg