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(經驗值)進行先判斷,如果小於就使用後面的倍增。這裏主要是防止內存的膨脹和浪費。