netty 是一非常优秀的nio通信框架,今天就来聊聊ByteBuf的。此图不全。。
既然java 提供了nio 的有buffer 的netty 为什么又要自己进行封装的呢。
主要是考虑了一下几点?java nio
1》原生的api 太少,操作数据不是很方便,
2〉原生的api 实例创建后,不能自动扩容,如果插入的数据超过剩余空间,会报异常。
那看下Netty 的ByteBuf 都做了哪些改变。
是通过 AbstractByteBuf 进行分析的。
1、操作指针的改变,java nio 读取操作使用的一个指针,操作比较麻烦,易出错。netty引入了readerIndex 和 writerIndex
+-------------------+------------------+------------------+
* | discardable bytes | readable bytes | writable bytes |
* | | (CONTENT) | |
* +-------------------+------------------+------------------+
* | | | |
* 0 <= readerIndex <= writerIndex <= capacity
2 看下readerIndex
@Override
public int readerIndex() {
return readerIndex;
}
@Override
public ByteBuf readerIndex(int readerIndex) {
if (readerIndex < 0 || readerIndex > writerIndex) {
throw new IndexOutOfBoundsException(String.format(
"readerIndex: %d (expected: 0 <= readerIndex <= writerIndex(%d))", readerIndex, writerIndex));
}
this.readerIndex = readerIndex;
return this;
}
初始化时readerIndex 和writerIndex 都是0 。readerIndex提供了两个方式进行获取读取数据的位置,默认和指定。在指定的时候做了readerIndex 值不能为0 或者 大于writerIndex 的判断。注意返回值的不同。
@Override
public int writerIndex() {
return writerIndex;
}
@Override
public ByteBuf writerIndex(int writerIndex) {
if (writerIndex < readerIndex || writerIndex > capacity()) {
throw new IndexOutOfBoundsException(String.format(
"writerIndex: %d (expected: readerIndex(%d) <= writerIndex <= capacity(%d))",
writerIndex, readerIndex, capacity()));
}
this.writerIndex = writerIndex;
return this;
}
这个同样是如此的。
@Override
public ByteBuf setIndex(int readerIndex, int writerIndex) {
if (readerIndex < 0 || readerIndex > writerIndex || writerIndex > capacity()) {
throw new IndexOutOfBoundsException(String.format(
"readerIndex: %d, writerIndex: %d (expected: 0 <= readerIndex <= writerIndex <= capacity(%d))",
readerIndex, writerIndex, capacity()));
}
this.readerIndex = readerIndex;
this.writerIndex = writerIndex;
return this;
}
@Override
public ByteBuf clear() {
readerIndex = writerIndex = 0;
return this;
}
这个 setIndex 和clear方法也很简单。
@Override
public boolean isReadable() {
return writerIndex > readerIndex;
}
@Override
public boolean isReadable(int numBytes) {
return writerIndex - readerIndex >= numBytes;
}
@Override
public boolean isWritable() {
return capacity() > writerIndex;
}
@Override
public boolean isWritable(int numBytes) {
return capacity() - writerIndex >= numBytes;
}
@Override
public int readableBytes() {
return writerIndex - readerIndex;
}
@Override
public int writableBytes() {
return capacity() - writerIndex;
}
@Override
public int maxWritableBytes() {
return maxCapacity() - writerIndex;
}
这个是操作buffer的一些检测。也很简单。
那看下buffer是如何控制写数据的,因为buffer 实现了7种基元类型的对写,用byte的来说。
@Override
public ByteBuf writeBytes(byte[] src, int srcIndex, int length) {
ensureWritable(length);
setBytes(writerIndex, src, srcIndex, length);
writerIndex += length;
return this;
}
@Override
public ByteBuf writeChar(int value) {
writeShort(value);
return this;
}
@Override
public ByteBuf writeFloat(float value) {
writeInt(Float.floatToRawIntBits(value));
return this;
}
@Override
public ByteBuf writeDouble(double value) {
writeLong(Double.doubleToRawLongBits(value));
return this;
}
在向buffer 写入时,首先进行了buffer剩余空间的检测。
@Override
public ByteBuf ensureWritable(int minWritableBytes) {
if (minWritableBytes < 0) {
throw new IllegalArgumentException(String.format(
"minWritableBytes: %d (expected: >= 0)", minWritableBytes));
}
if (minWritableBytes <= writableBytes()) {
return this;
}
if (minWritableBytes > maxCapacity - writerIndex) {
throw new IndexOutOfBoundsException(String.format(
"writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
writerIndex, minWritableBytes, maxCapacity, this));
}
// Normalize the current capacity to the power of 2.
int newCapacity = calculateNewCapacity(writerIndex + minWritableBytes);
// Adjust to the new capacity.
capacity(newCapacity);
return this;
}
首先进行了检测,如果小于0 ,直接抛异常,如果是在可写空间范围,直接返回,如果是大于最大容量,直接抛异常。
这个最大容量并不是一成不变的会根据方法
int newCapacity = calculateNewCapacity(writerIndex + minWritableBytes);
进行计算的。
private int calculateNewCapacity(int minNewCapacity) {
final int maxCapacity = this.maxCapacity;
final int threshold = 1048576 * 4; // 4 MiB page
if (minNewCapacity == threshold) {
return threshold;
}
// If over threshold, do not double but just increase by threshold.
if (minNewCapacity > threshold) {
int newCapacity = minNewCapacity / threshold * threshold;
if (newCapacity > maxCapacity - threshold) {
newCapacity = maxCapacity;
} else {
newCapacity += threshold;
}
return newCapacity;
}
// Not over threshold. Double up to 4 MiB, starting from 64.
int newCapacity = 64;
while (newCapacity < minNewCapacity) {
newCapacity <<= 1;
}
return Math.min(newCapacity, maxCapacity);
}
使用了threshold 作为阈值进行计算,
如果插入的数据正好是4M,则直接返回,如果大于的话进行判断,进行一系列的判断,
但是这个代码的意思是newCapacity的容量必须是2的倍数。
前面除是只能是2的倍数整除。
计算buf的容量,这里做了内存优化配置。首先判断写入的内存是否过大,使用4M(经验值)进行先判断,如果小于就使用后面的倍增。这里主要是防止内存的膨胀和浪费。