Netty 源碼基礎篇-- ByteBuf

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

 

 

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