這個類的核心目的就是根據從底層socket讀取的字節數量,動態調整分配空間,以及是否繼續從socket讀取字節流
@Override
public final void read() {
final ChannelConfig config = config();
if (shouldBreakReadReady(config)) {
clearReadPending();
return;
}
//每個channel對應一個PPLine
final ChannelPipeline pipeline = pipeline();
//ByteBuf分配器
final ByteBufAllocator allocator = config.getAllocator();
//容量計算器
final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
//重置,把之前計數的值全部清空
allocHandle.reset(config);
ByteBuf byteBuf = null;
boolean close = false;
try {
do {
//分配內存,關鍵在於計算分配內存的大小(小了不夠,大了浪費)
byteBuf = allocHandle.allocate(allocator);
//doReadBytes,從socket讀取字節到byteBuf,返回真實讀取數量
//更新容量計算器
allocHandle.lastBytesRead(doReadBytes(byteBuf));
//如果小於0 則socket關閉,如果等於0則沒讀取到數據
if (allocHandle.lastBytesRead() <= 0) {
// nothing was read. release the buffer.
//釋放資源
byteBuf.release();
byteBuf = null;
//如果小於0則意味着socket關閉
close = allocHandle.lastBytesRead() < 0;
if (close) {
// There is nothing left to read as we received an EOF.
readPending = false;
}
break;
}
//增加循環計數器
allocHandle.incMessagesRead(1);
readPending = false;
//把讀取到的數據,交給管道去處理
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
//判斷是否繼續從socket讀取數據
} while (allocHandle.continueReading());
//讀取完成後調用readComplete,重新估算內存分配容量
allocHandle.readComplete();
//事件激發
pipeline.fireChannelReadComplete();
//如果需要關閉,則處理關閉
if (close) {
closeOnRead(pipeline);
}
} catch (Throwable t) {
handleReadException(pipeline, byteBuf, t, close, allocHandle);
} finally {
// Check if there is a readPending which was not processed yet.
// This could be for two reasons:
// * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
// * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
//
// See https://github.com/netty/netty/issues/2254
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
}
public abstract class MaxMessageHandle implements ExtendedHandle {
private ChannelConfig config;
private int maxMessagePerRead;
private int totalMessages;
private int totalBytesRead;
private int attemptedBytesRead;
private int lastBytesRead;
private final boolean respectMaybeMoreData = DefaultMaxMessagesRecvByteBufAllocator.this.respectMaybeMoreData;
private final UncheckedBooleanSupplier defaultMaybeMoreSupplier = new UncheckedBooleanSupplier() {
@Override
public boolean get() {
return attemptedBytesRead == lastBytesRead;
}
};
/**
* Only {@link ChannelConfig#getMaxMessagesPerRead()} is used.
*/
@Override
public void reset(ChannelConfig config) {
//每次底層socket開始讀取數據時調用,清空狀態。
this.config = config;
maxMessagePerRead = maxMessagesPerRead();
totalMessages = totalBytesRead = 0;
}
@Override
public ByteBuf allocate(ByteBufAllocator alloc) {
//創建一個ByteBuf,重點在於guess方法
//最理想的狀態是分配的容量剛好可以容納本次socket的緩衝區字節
//分配空間過大浪費內存,過小需要循環讀取多次
return alloc.ioBuffer(guess());
}
@Override
public final void incMessagesRead(int amt) {
//每次通過socket讀取消息時+1,記錄從socket讀取了幾次數據
totalMessages += amt;
}
@Override
public void lastBytesRead(int bytes) {
//每次通過socket讀取字節後會記錄字節數
lastBytesRead = bytes;
if (bytes > 0) {
//累計總數
totalBytesRead += bytes;
}
}
@Override
public final int lastBytesRead() {
//最後一次從socket讀取的字節數
return lastBytesRead;
}
@Override
public boolean continueReading() {
//計算是否需要繼續從底層socket讀取數據
return continueReading(defaultMaybeMoreSupplier);
}
//maybeMoreDataSupplier的實現是判斷attemptedBytesRead == lastBytesRead;
//attemptedBytesRead是bytebuf的可寫空間,也就是希望讀取多少字節
//lastBytesRead是真實從socket讀取到的字節數,如果一致說明bytebuf寫滿了,可能後續還有字節
@Override
public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
//config.isAutoRead() 是否配置了自動讀取
//totalMessages < maxMessagePerRead 總共讀取的次數小於閾值
//totalBytesRead > 0 確實讀取到了字節
return config.isAutoRead() &&
(!respectMaybeMoreData || maybeMoreDataSupplier.get()) &&
totalMessages < maxMessagePerRead &&
totalBytesRead > 0;
}
@Override
public void readComplete() {
}
@Override
public int attemptedBytesRead() {
return attemptedBytesRead;
}
@Override
public void attemptedBytesRead(int bytes) {
attemptedBytesRead = bytes;
}
protected final int totalBytesRead() {
return totalBytesRead < 0 ? Integer.MAX_VALUE : totalBytesRead;
}
}
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.channel;
import java.util.ArrayList;
import java.util.List;
import static io.netty.util.internal.ObjectUtil.checkPositive;
import static java.lang.Math.max;
import static java.lang.Math.min;
/**
* The {@link RecvByteBufAllocator} that automatically increases and
* decreases the predicted buffer size on feed back.
* <p>
* It gradually increases the expected number of readable bytes if the previous
* read fully filled the allocated buffer. It gradually decreases the expected
* number of readable bytes if the read operation was not able to fill a certain
* amount of the allocated buffer two times consecutively. Otherwise, it keeps
* returning the same prediction.
*/
public class AdaptiveRecvByteBufAllocator extends DefaultMaxMessagesRecvByteBufAllocator {
//最小字節大小
static final int DEFAULT_MINIMUM = 64;
//初始化字節大小
static final int DEFAULT_INITIAL = 1024;
//最大字節大小
static final int DEFAULT_MAXIMUM = 65536;
//遞增,遞減的索引大小
private static final int INDEX_INCREMENT = 4;
private static final int INDEX_DECREMENT = 1;
//容量數組16,32,48...512...1024...1073741824 成倍增長
private static final int[] SIZE_TABLE;
static {
List<Integer> sizeTable = new ArrayList<Integer>();
//從16開始,每次增加16,一直到496
for (int i = 16; i < 512; i += 16) {
sizeTable.add(i);
}
//從512開始,每次翻倍,一直到溢出爲負數,最大值是1073741824
for (int i = 512; i > 0; i <<= 1) {
sizeTable.add(i);
}
//把list當中的值放入數組中
SIZE_TABLE = new int[sizeTable.size()];
for (int i = 0; i < SIZE_TABLE.length; i ++) {
SIZE_TABLE[i] = sizeTable.get(i);
}
}
/**
* @deprecated There is state for {@link #maxMessagesPerRead()} which is typically based upon channel type.
*/
@Deprecated
public static final AdaptiveRecvByteBufAllocator DEFAULT = new AdaptiveRecvByteBufAllocator();
//給定一個size,查找數組中相同容量值的下標位置,採用二分查找法
private static int getSizeTableIndex(final int size) {
for (int low = 0, high = SIZE_TABLE.length - 1;;) {
if (high < low) {
return low;
}
if (high == low) {
return high;
}
//這裏+會優先計算,然後在右移(除2)
//mid取中間位置
int mid = low + high >>> 1;
int a = SIZE_TABLE[mid]; //中間位置值
int b = SIZE_TABLE[mid + 1];//中間位置+1的值
if (size > b) {
//這種情況low需要變大
low = mid + 1;
} else if (size < a) {
//這種情況high需要變小
high = mid - 1;
} else if (size == a) {
//返回下標
return mid;
} else {
//返回下標
return mid + 1;
}
}
}
private final class HandleImpl extends MaxMessageHandle {
private final int minIndex; //最小容量數組下標
private final int maxIndex;//最大容量數組下標
private int index;//初始化容量下標
private int nextReceiveBufferSize; //下次分配的內存大小,默認SIZE_TABLE[index]
private boolean decreaseNow;
HandleImpl(int minIndex, int maxIndex, int initial) {
this.minIndex = minIndex;
this.maxIndex = maxIndex;
index = getSizeTableIndex(initial);
nextReceiveBufferSize = SIZE_TABLE[index];
}
@Override
public void lastBytesRead(int bytes) {
// If we read as much as we asked for we should check if we need to ramp up the size of our next guess.
// This helps adjust more quickly when large amounts of data is pending and can avoid going back to
// the selector to check for more data. Going back to the selector can add significant latency for large
// data transfers.
//真實讀取的字節數與期望(bytebuf的容量)一樣多
//說明一次讀取已經寫滿bytebuf,則應該調整guess的大小
if (bytes == attemptedBytesRead()) {
record(bytes);
}
//父類記錄最後一次讀取字節數
super.lastBytesRead(bytes);
}
@Override
public int guess() {
//分配內存空間bytebuf的大小
return nextReceiveBufferSize;
}
//actualReadBytes=從socket讀取的字節數
private void record(int actualReadBytes) {
//如果讀取字節數 小於等於SIZE_TABLE[index-1]則有必要減少分配空間
if (actualReadBytes <= SIZE_TABLE[max(0, index - INDEX_DECREMENT)]) {
if (decreaseNow) {
//把index減少後不能少於minIndex
index = max(index - INDEX_DECREMENT, minIndex);
//重新賦值,相當於減少了下次分配的空間大小
nextReceiveBufferSize = SIZE_TABLE[index];
decreaseNow = false;
} else {
decreaseNow = true;
}
//如果讀取字節數大於等於分配的空間,則說明有不要增大分配空間
} else if (actualReadBytes >= nextReceiveBufferSize) {
//把index增大,不超過maxIndex
index = min(index + INDEX_INCREMENT, maxIndex);
//重新賦值,相當於增大了下次分配的空間
nextReceiveBufferSize = SIZE_TABLE[index];
decreaseNow = false;
}
}
@Override
public void readComplete() {
//在讀取完成後,傳入所有讀取的字節總數,進行容量調整
record(totalBytesRead());
}
}
private final int minIndex;
private final int maxIndex;
private final int initial;
/**
* Creates a new predictor with the default parameters. With the default
* parameters, the expected buffer size starts from {@code 1024}, does not
* go down below {@code 64}, and does not go up above {@code 65536}.
*/
public AdaptiveRecvByteBufAllocator() {
this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM);
}
/**
* Creates a new predictor with the specified parameters.
*
* @param minimum the inclusive lower bound of the expected buffer size
* @param initial the initial buffer size when no feed back was received
* @param maximum the inclusive upper bound of the expected buffer size
*/
public AdaptiveRecvByteBufAllocator(int minimum, int initial, int maximum) {
checkPositive(minimum, "minimum");
if (initial < minimum) {
throw new IllegalArgumentException("initial: " + initial);
}
if (maximum < initial) {
throw new IllegalArgumentException("maximum: " + maximum);
}
//根據minimum查找數組下標
int minIndex = getSizeTableIndex(minimum);
if (SIZE_TABLE[minIndex] < minimum) {
this.minIndex = minIndex + 1;
} else {
this.minIndex = minIndex;
}
//根據maximum查找數組下標
int maxIndex = getSizeTableIndex(maximum);
if (SIZE_TABLE[maxIndex] > maximum) {
this.maxIndex = maxIndex - 1;
} else {
this.maxIndex = maxIndex;
}
this.initial = initial;
}
@SuppressWarnings("deprecation")
@Override
public Handle newHandle() {
return new HandleImpl(minIndex, maxIndex, initial);
}
@Override
public AdaptiveRecvByteBufAllocator respectMaybeMoreData(boolean respectMaybeMoreData) {
super.respectMaybeMoreData(respectMaybeMoreData);
return this;
}
}