##什么是Buffer
buffer中文名又叫缓冲区,按照维基百科的解释,是”在数据传输时,在内存里开辟的一块临时保存数据的区域”。它其实是一种化同步为异步的机制,可以解决数据传输的速率不对等以及不稳定的问题。
Buffer在其他场景的具体应用
根据这个定义,我们可以知道涉及I/O(特别是I/O写)的地方,基本会有buffer的存在。
就Java来说,我们非常熟悉的Old I/O–InputStream&OutputStream系列API,基本都是在内部使用到了buffer。
Java课程老师就教过,outputStream.write()只将内容写入了buffer,必须调用outputStream.flush(),才能保证数据写入生效!
而NIO中则直接将buffer这个概念封装成了对象,其中最常用的大概是ByteBuffer了。于是使用方式变为了:将数据写入Buffer,flip()一下,然后将数据读出来。于是,buffer的概念更加深入人心了!
Buffer的意义
TCP报文有个比较大的特点,就是它传输的时候,会先把应用层的数据项拆开成字节,然后按照自己的传输需要,选择合适数量的字节进行传输。什么叫”自己的传输需要”?首先TCP包有最大长度限制,那么太大的数据项肯定是要拆开的。其次因为TCP以及下层协议会附加一些协议头信息,如果数据项太小,那么可能报文大部分都是没有价值的头信息,这样传输是很不划算的。因此有了收集一定数量的小数据,并打包传输的Nagle算法(这个东东在HTTP协议里会很讨厌,Netty里可以用setOption(“tcpNoDelay”, true)关掉它)。
TCP传输分次
发送时,我们这样分3次写入(‘|’表示两个buffer的分隔):
ABC | DEF | GHI
接收时,可能变成了这样:
AB | CDEFG | H | I
为什么需要ByteBuf
当需要与远程进行交互时,需要以字节码发送/接收数据。由于各种原因,一个高效、方便、易用的数据接口是必须的
ByteBuf用途
ByteBuf有两部分:一个用于读,一个用于写。ByteBuf的默认最大容量限制是Integer.MAX_VALUE
ByteBuf类似于一个字节数组,最大的区别是读和写的索引可以用来控制对缓冲区数据的访问
ByteBuf类型区分
Heap Buffer(堆缓冲区)
存储于: 堆内存
提供了直接访问数组的方法,ByteBuf.array 来获取byte[]数据, 访问非堆缓冲区ByteBuf的数组会导致 UnsupportedOpeaarationException,可以用ByteBuf.hasArray()进行检查
Direct Buffer(直接缓冲区)
在堆之外直接分内存,使用时需要考虑最大内存容量以及如何西安至它,直接缓冲区在传输数据时性能很好,但是分配和释放过程较为复杂,Netty使用内存池来解决这样的问题,直接缓冲区不支持数组直接访问数据,但是我们可以间接的访问数据数组
1 | ByteBuf directBuf = Unpooled.directBuffer(16); |
Composite Buffer(复合缓冲区)
复合缓冲区,我们可以创建多个不同的 ByteBuf,然后提供一个这些 ByteBuf
有点类似组合模式
Netty 提供了 CompositeByteBuf 类来处理复合缓冲区,CompositeByteBuf只是一个视图,CompositeByteBuf.hasArray()总是返回 false,因为它 可能包含一些直接或间接的不同类型的 ByteBuf