ByteBuf
ByteBuf是Netty提供的,与JDK的ByteBuffer相比,ButeBuf具有更加卓越的功能,使用也更加灵活。
1、ByteBuf的API
ByteBuf提供读访问索引(readerIndex)和写访问索引(WriterIndex)来控制字节数组。ByteBuf的API具有以下的特点
- 允许用户自定义缓冲区类型扩展
- 通过内置的复合缓冲区类型实现透明的零拷贝
- 容量可按需增长
- 读写这两种模式之间不需要再调用JDKByteBuffer的filp()方法进行切换
- 读和写使用不同的索引
- 支持方法链式调用
- 执行引用计数
- 支持池化
2、ByteBuf类 —— Netty的数据容器
2.1 ByteBuf参数
- capacity:指定ByteBuf初始化容量大小
- maxCapacity: ByteBuf最大容量(允许扩容的最大值)
- readerIndex:读索引
- writerIndex:写索引
ByteBuf维护readIndex和writerIndex索引;
当readIndex > writerIndex时,就会抛出异常“IndexOutOfBoundsExcpetion”;
Byteuf容量 = writerIndex;
ByeBuf刻度容量 = writerIndex - readIndex;
readXXX()和writerXXX()方法将会自动推进其对应的索引;
getXXX()和setXXX()方法对readXXX()和writerXXX()无影响
2.2 ByteBuf的使用模式
ByteBuf本质是有一个由不同的索引分别控制读取和写入的字节数组。
ByteBuf有三种模式:
- 堆缓冲区模式(Heap Buffer)
- 直接缓冲区模式(Direct Buffer)
- 复合缓冲区模式(Composite Buffer)
2.2.1 堆缓冲区模式(Heap Buffer)
堆缓冲区模式:又被称为 支撑数组(backing array)。将数据存放在JVM的堆内存中,通过将数据存储在数组中实现
- 优点:数据存在在JVM堆内存中,可以快速创建和释放,并且支持数组的快速访问。
- 缺点:每次数据进行I/O传输时,都需要将数据拷贝到缓冲区中
String str = "hello ByteBuf";
ByteBuf headBuffer = Unpooled.buffer(16);
headBuffer.writeBytes(str.getBytes());
2.2.2 直接缓冲区模式(Direct Buffer)
Direct Buffer属于堆外内存,不会占用堆内存。适用于套接字传输过程,避免了数据从内不缓冲区拷贝到直接缓冲区的过程,性能更好
- 优点:使用Socket传输数据时性能更好,避免了数据从JVM堆内存拷贝到直接缓冲区的过程,提升了性能
- 缺点:相对于堆缓冲区而言,Direct Buffer分配内存空间和释放内存的开销更大
- 使用场景:对于涉及大量I/O数据读写,建议使用Direct Buffer。而用于对消息进行编解码,建议使用Heap Buffer
ByteBuf directBuffer = Unpooled.directBuffer(16);
String str = "hello direct buffer";
directBuffer.writeBytes(str.getBytes());
int length = directBuffer.readableBytes();
byte[] bytes = new byte[length];
directBuffer.getBytes(directBuffer.readerIndex(), bytes);
System.out.println(length);
System.out.println(bytes.length);
System.out.println(new String(bytes));
2.2.3 复合缓冲区模式(Composite Buffer)
Composite Buffer是Netty特有的缓冲区。类似于提供一个或多个ByteBuf的组合视图,可以根据需要添加和删除不同类型的ByteBuf
- Composite Buffer:是一种组合视图,提供一种方式让使用者自有组合多个ByteBuf,避免了拷贝和分配新缓冲区
- Composite Buffer:不支持访问其支持数组,如果要访问,需要先将内容拷贝到堆内存中,在进行访问
- COmposite Buffer由头部和主体组成,头部和主体组合在一起没有进行任何复制过程,仅仅是创建了一个视图
CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer();
ByteBuf headBuffer = Unpooled.buffer(); // 也可以使用直接内存
headBuffer.writeBytes("你好".getBytes());
ByteBuf bodyBuffer = Unpooled.directBuffer(); // 也可以使用堆内存
bodyBuffer.writeBytes("堆内存缓冲区".getBytes());
compositeByteBuf.addComponents(headBuffer,bodyBuffer);
for (ByteBuf buf : compositeByteBuf){
if (buf.hasArray()){ // 是堆内存缓冲区
System.out.println(new String(buf.array()));
}else { // 是直接内存缓冲区
int length = bodyBuffer.readableBytes(); // 缓冲区长度
byte[] bytes = new byte[length];
buf.getBytes(buf.readerIndex(), bytes);
System.out.println(new String(bytes));
}
}