Netty源碼分析-AdaptiveRecvByteBufAllocator

這個類的核心目的就是根據從底層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;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章