国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Netty ByteBuf

whataa / 795人閱讀

摘要:提供了作為它的字節容器但是這個類使用起來過于復雜而且也有些繁瑣的的代替品是的的數據處理通過兩個組件暴露下面是的優點它可以被用戶自定義的緩沖區類擴展通過內置的復合緩沖區類型實現了透明的零拷貝容量可以按需增長在讀和寫這兩種模式之間雀環不需要調用

Java NIO 提供了 ByteBuffer 作為它的字節容器, 但是這個類使用起來過于復雜, 而且也有些繁瑣.

Netty 的 ByteBuffer 的代替品是 ByteBuf.

ByteBuf 的 API

Netty 的數據處理 API 通過兩個組件暴露

public abstract class ByteBuf implements ReferenceCounted, Comparable
public interface ByteBufHolder extends ReferenceCounted

下面是 ByteBuf API 的優點:

它可以被用戶自定義的緩沖區類擴展;

通過內置的復合緩沖區類型實現了透明的零拷貝;

容量可以按需增長;

在讀和寫這兩種模式之間雀環不需要調用 ByteBuffer 的 flip() 方法;

讀和寫試用了不同的索引;

支持方法的鏈式調用;

支持引用計數;

支持池化.

它是如何工作的

ByteBuf 維護了兩個不同的索引: 一個用于讀取, 一個用于寫入. 當你從 ByteBuf 讀取時, 它的 readerIndex 將會被遞增到讀取的字節數. 同樣的, 當你寫入 ByteBuf 時, 它的 writerIndex 也會被遞增.

要了解這些索引兩兩之間的關系, 請考慮一下, 如果打算讀取字節直到 readerIndex 達到和 writerIndex 同樣的值時會發生什么.

在那時, 你將會到達 "可以讀取的" 數據的末尾. 就如同試圖讀取超出數組末尾的數據一樣, 會觸發一個 IndexOutOfBoundsException.

名稱以 read 或 write 開頭的 ByteBuf 方法, 將會推進其對應的索引, 而名稱以 set 或 get 開頭的操作則不會. 后面的這些方法將在作為一個參數傳入的一個相對索引上執行操作.

可以指定 ByteBuf 的最大容量. 默認的限制是 Interger.MAX_VALUE.
ByteBuf 的使用模式 堆緩沖區

最常用的 ByteBuf 模式是將數據存儲在 JVM 的堆空間中. 這種模式被稱為 支撐數組 , 它能在沒有使用池化的情況下提供快速的分配和釋放. 代碼如下:

public static void main(String args[]) {
    ByteBuf heapBuf = Unpooled.copiedBuffer("heap space", CharsetUtil.UTF_8);
    if (heapBuf.hasArray()) { // 檢查 ByteBuf 是否有一個支持數組
        byte[] array = heapBuf.array();
        int offset = heapBuf.arrayOffset() + heapBuf.readerIndex();// 計算第一個字節的偏移量
        int length = heapBuf.readableBytes();// 計算可讀字節數
        System.out.println(Arrays.toString(array));
        System.out.println(offset);
        System.out.println(length);
    } else {
        System.out.println("No Heap Array");
    }
}
hasArray() 返回 false 時嘗試訪問支持數組會拋出 UnsupportedOperationException. 這個用法與JDK的 ByteBuffer 類似.
直接緩沖區

直接緩沖區是另外一種 ByteBuf 模式.

直接緩沖區的內容并不是駐留在 Java 的堆上, 利用 java.nio.ByteBuffer 是借助于 JVM 調用操作系統的底層通信函數, 直接操作直接緩沖區可以減少中間緩沖區的復制操作, 進而提供程序性能, 其目的是:

通過免去中間交換的內存拷貝(從程序內存拷貝到系統內存), 提升IO處理速度; 直接緩沖區的內容可以駐留在垃圾回收掃描的堆區以外.

DirectBuffer 在 -XX:MaxDirectMemorySize=xxM 大小限制下, 使用 Heap 之外的內存, GC對此 ”無能為力” , 也就意味著規避了在高負載下頻繁的 GC 過程對應用線程的中斷影響.

但是直接緩沖區的缺點是在內存空間的分配和釋放上比堆緩沖區更復雜, 另外一個缺點是如果要將數據傳遞給遺留代碼處理, 因為數據不是在堆上, 你可能不得不作出一個副本, 如下:

public static void main(String args[]) {
    ByteBuf directBuf = Unpooled.directBuffer(100);
    directBuf.writeBytes("direct buffer".getBytes());
    if (!directBuf.hasArray()) { // 檢查 ByteBuf 是否由數組支撐. 如果不是, 則這是一個直接緩沖區
        int length = directBuf.readableBytes(); // 獲取可讀字節數
        byte[] array = new byte[length]; // 分配一個新的數組來保存具有該長度的字節數據
        directBuf.getBytes(directBuf.readerIndex(), array); // 將字節復制到該數組
        System.out.println(Arrays.toString(array));
        System.out.println(length);
    }
}
復合緩沖區

復合緩沖區為多個 ByteBuf 提供一個聚合視圖, 可以根據需要添加或者刪除 ByteBuf 實例.

Netty 提供了 ByteBuf 的子類 CompositeByteBuf 類來處理復合緩沖區, CompositeByteBuf 只是一個視圖.

CompositeByteBuf 中的 ByteBuf 實例可能同時包含直接內存分配和非直接內存分配.

如果其中只有一個實例, 那么對 CompositeByteBuf 上的 hasArray() 方法的調用將返回該組件上的 hasArray()方法的值; 否則它將返回 false. 因為有可能包含直接緩沖區或堆緩沖區.

例如, 一條消息由 headerbody 兩部分組成, 將 headerbody 組裝成一條消息發送出去, 可能 body 相同, 只是 header 不同, 使用 CompositeByteBuf 就不用每次都重新分配一個新的緩沖區.

下圖顯示CompositeByteBuf 組成 headerbody:

下面代碼顯示了使用 JDK 的 ByteBuffer 的一個實現. 兩個 ByteBuffer 的數組創建保存消息的組件,第三個創建用于保存所有數據的副本.

// 使用數組保存消息的各個部分
ByteBuffer[] message = { header, body };

// 使用副本來合并這兩個部分
ByteBuffer message2 = ByteBuffer.allocate(
        header.remaining() + body.remaining());
message2.put(header);
message2.put(body);
message2.flip();

這種做法顯然是低效的; 分配和復制操作不是最優的方法, 操縱數組使代碼顯得很笨拙.

下面看使用 CompositeByteBuf 的改進版本.

/**
 * Netty 通過一個 ByteBuf 子類——CompositeByteBuf——實現了組合模式,它提供了一
 個將多個緩沖區表示為單個合并緩沖區的虛擬表示
 */
public class CompositeBuf {
    public static void main(String args[]){
        CompositeByteBuf messageBuf = Unpooled.compositeBuffer();
        ByteBuf headerBuf = Unpooled.copiedBuffer("head", CharsetUtil.UTF_8); // can be backing or direct
        ByteBuf bodyBuf = Unpooled.copiedBuffer("body", CharsetUtil.UTF_8); // can be backing or direct
        messageBuf.addComponents(headerBuf, bodyBuf);
        System.out.println("Remove Head Before------------------");
        printCompositeBuffer(messageBuf);
        for (ByteBuf buf : messageBuf) {
            System.out.println(buf.toString(CharsetUtil.UTF_8));
        }
        messageBuf.removeComponent(0); // remove the header
        System.out.println("Remove Head After------------------");
        printCompositeBuffer(messageBuf);
        for (ByteBuf buf : messageBuf) {
            System.out.println(buf.toString(CharsetUtil.UTF_8));
        }
    }

    public static void printCompositeBuffer(CompositeByteBuf compBuf){
        int length = compBuf.readableBytes();
        byte[] array = new byte[length];
        compBuf.getBytes(compBuf.readerIndex(), array);
        System.out.println (Arrays.toString(array));
        System.out.println (length);
    }
}

Netty 嘗試使用 CompositeByteBuf 優化 socket I/O 操作, 消除原生 JDK 中可能存在的的性能低和內存消耗問題.

雖然這是在 Netty 的核心代碼中進行的優化, 并且是不對外暴露的, 但是作為開發者還是應該意識到其影響.

ByteBuf分配

從空間初始化方式上來分, ByteBuf 分為: 緩存方式分配和非緩存方式分配.

5.1 使用 ByteBufAllocator 接口分配

為了降低分配和釋放內存的開銷, Netty 通過 interface ByteBufAllocator 實現了 ByteBuf 的池化, 它可以用來分配我們所描述過的任意類型的 ByteBuf 實例 (基于堆緩沖區的, 基于直接緩沖區的, 基于復合緩沖區的).

// 返回一個基于堆或者直接內存存儲的 ByteBuf
ByteBuf buffer();
ByteBuf buffer(int initialCapacity);
ByteBuf buffer(int initialCapacity, int maxCapacity);

// 返回一個基于堆內存的 ByteBuf
ByteBuf heapBuffer();
ByteBuf heapBuffer(int initialCapacity);
ByteBuf heapBuffer(int initialCapacity, int maxCapacity);

// 返回一個基于直接內存的 ByteBuf
ByteBuf directBuffer();
ByteBuf directBuffer(int initialCapacity);
ByteBuf directBuffer(int initialCapacity, int maxCapacity);

// 通過設置一個最大值, 基于堆內存或直接內存, 返回一個混合緩沖區.
CompositeByteBuf compositeBuffer(int maxNumComponents);
CompositeByteBuf compositeHeapBuffer();
CompositeByteBuf compositeHeapBuffer(int maxNumComponents);
CompositeByteBuf compositeDirectBuffer();
CompositeByteBuf compositeDirectBuffer(int maxNumComponents);

// 返回一個用于套接字 I/O 操作的 ByteBuf
ioBuffer()
ioBuffer() 重點 
默認的, 當所運行的環境具有 sun.misc.Unsafe 支持時, 返回基于直接內存的 ByteBuf, 否則返回一個基于堆內存的 ByteBuf; 當指定使用 PreferHeapByteBufAllocator 時, 則只會返回基于堆內存的 ByteBuf.

可以通過 Channle(每個都可以有一個不同的 ByteBufAllocator 實例) 或者 ChannleHandlerContext 獲取到 ByteBufAllocator.

Netty 提供了兩種 ByteBufAllocator 的實現:

Channel channel = ...;
ByteBufAllocator byteBufAllocator = channel.alloc();

ChannelHandlerContext channelHandlerContext = ...;
ByteBufAllocator bufAllocator = channelHandlerContext.alloc();

前者池化了ByteBuf 的實例以提高性能并最大限度地減少內存碎片(PooledByteBufAllocator 默認).
后者不池化ByteBuf實例, 并且在每次它被調用時都會返回一個新的實例(UnpooledByteBufAllocator).

使用 Unpooled 緩沖區分配

可能某些情況下, 你未能獲取一個到 ByteBufAllocator 的引用. 對于這種情況, Netty 提供了一個簡單的稱為 Unpooled(封裝了 UnpooledByteBufAllocator) 的工具類, 它提供了靜態的輔助方法來創建未池化的 ByteBuf 實例. 提供的方法如下:

buffer: 返回一個未池化的基于堆內存存儲的 ByteBuf.

directBuffer: 返回一個未池化的基于直接內存存儲的 ByteBuf

wrappedBuffer: 返回一個包裝了給定數據的 ByteBuf

copiedBuffer: 返回一個復制了給定數據的 ByteBuf

ByteBuf directBuf = Unpooled.directBuffer(100);
directBuf.writeBytes("direct buffer".getBytes());
System.out.println(directBuf.indexOf(0,directBuf.readableBytes(), (byte) "u"));
Unpooled 類還使得 ByteBuf 同樣可用于那些并不需要 Netty 的其他組件的非網絡項目, 使得其能得益于高性能的可擴展的緩沖區 API.
隨機訪問索引

如同在普通的 Java 字節數組中一樣, ByteBuf 的索引是從零開始的: 第一個字節的索引是0, 最后一個字節的索引總是 capacity() - 1, 可以通過 index 進行訪問:

public static void main(String args[]) {
    ByteBuf directBuf = Unpooled.directBuffer(100);
    directBuf.writeBytes("direct buffer".getBytes());
    System.out.println(directBuf.getByte(5));
}
這種方式并不會改變 readerIndexweiterIndex. 如果有需要, 也可以通過調用 readerIndex(index) 或者 writerIndex(index) 來手動移動這兩者.
順序訪問索引

雖然 ByteBuf 同時具有讀索引和寫索引, 但是 JDK 的 ByteBuffer 卻只有一個索引, 這也就是為什么必須調用 flip() 方法來在讀模式和寫模式之間進行切換的原因. ByteBuf 被兩個索引劃分成 3 個區域.

可丟棄的字節

可丟棄字節的分段包含了已經被讀過的字節. 通過調用 discardReadBytes() 方法, 可以丟棄它們并回收空間. 這個分段的初始大小為 0, 存儲在 readerIndex 中, 會隨著 read 操作的執行而增加.

下圖展示的緩沖區上調用 discardReadBytes() 方法后的結果. 可以看到, 可丟棄字節分段中的空間已經變為可寫的了.

注意, 在調用 discardReadBytes() 之后, 對可寫分段的內容并沒有任何的保證.

頻繁地調用 discardReadBytes() 方法可以確保可寫分段的最大化, 但是這將極有可能會導致內存復制, 因為可讀字節 (圖中標記為 CONTENT 的部分) 必須被移動到緩沖區的開始位置. 我們建議只在有真正需要的時候才這樣做, 例如, 當內存非常寶貴的時候.

可讀字節

ByteBuf 的可讀字節分段存儲了實際數據. 新分配的、包裝的或者復制的緩沖區默認 readerIndex 值為 0. 任何名稱以 read 或者 skip 開頭的操作都將檢索或者跳過位于當前 readerIndex 的數據, 并且將它增加已讀字節數.

如果嘗試在緩沖區的可讀字節數已經耗盡時從中讀取數據, 那么將會引發一個 IndexOutOfBoundsException.

可寫字節

可寫字節分段是指一個擁有未定義內容的、寫入就緒的內存區域. 新分配的緩沖區的 writerIndex 的默認值為 0. 任何名稱以 write 開頭的操作都將從當前的 writerIndex 處開始寫數據, 并將它增加已經寫入的字節數.

如果寫操作的目標也是 ByteBuf, 并且沒有指定源索引的值, 則源緩沖區的 readerIndex 也同樣會被增加相同的大小.

如果嘗試往目標寫入超過目標容量的數據, 將會引發一個 IndexOutOfBoundException.

例子
        ByteBuf directBuf = Unpooled.directBuffer(100);
        directBuf.writeBytes("direct buffer".getBytes());
        System.out.println("可寫字節容量:"+directBuf.writableBytes());
        System.out.println("初始化可讀字節:"+directBuf.readableBytes());
        System.out.println("初始化可丟棄字節:"+directBuf.readerIndex()+"
");
        directBuf.readBytes(2);
        System.out.println("讀取兩個字節"+"
");
        System.out.println("讀取后可寫字節容量:"+directBuf.writableBytes());
        System.out.println("讀取后可讀字節:"+directBuf.readableBytes());
        System.out.println("讀取后可丟棄字節:"+directBuf.readerIndex()+"
");
        directBuf.discardReadBytes();
        System.out.println("執行discardReadBytes后可寫字節容量:"+directBuf.writableBytes());
        System.out.println("執行discardReadBytes后可讀字節:"+directBuf.readableBytes());
        System.out.println("執行discardReadBytes后可丟棄字節:"+directBuf.readerIndex());

輸出為:

可寫字節容量:87
初始化可讀字節:13
初始化可丟棄字節:0

讀取兩個字節

讀取后可寫字節容量:87
讀取后可讀字節:11
讀取后可丟棄字節:2

執行discardReadBytes后可寫字節容量:89
執行discardReadBytes后可讀字節:11
執行discardReadBytes后可丟棄字節:0
索引管理

索引管理的相關操作如下:

可以通過調用 markReaderIndex()markWriterIndex()resetWriterIndex()resetReaderIndex() 來標記和重置 ByteBufreaderIndexwriterIndex.

可以通過調用 readerIndex(int) 或者 writerIndex(int) 來將索引移動到指定位置. 試圖將任何一個索引設置到一個無效的位置都將導致一個 IndexOutOfBoundsException.

可以通過調用 clear() 方法來將 readerIndexwriterIndex 都設置為 0. 調用 clear() 比調用 discardReadBytes() 輕量得多, 因為它將只是重置索引而不會復制任何的內存.

示例如下:

        ByteBuf directBuf = Unpooled.directBuffer(100);
        directBuf.writeBytes("direct buffer".getBytes());
        System.out.println("初始化可讀字節:"+directBuf.readableBytes());
        directBuf.markReaderIndex();
        System.out.println("執行markReaderIndex"+"
");//標記讀索引
        directBuf.readBytes(2);
        System.out.println("讀取兩個字節"+"
");
        System.out.println("讀取后可讀字節:"+directBuf.readableBytes());
        directBuf.resetReaderIndex();//恢復讀索引
        System.out.println("執行resetReaderIndex后可讀字節:"+directBuf.readableBytes());
        directBuf.clear();
        System.out.println("執行clear后可讀字節:"+directBuf.readableBytes());
        directBuf.readBytes(2);//可讀字節變為0,此時再讀取會拋出IndexOutOfBoundsException

輸出如下:

初始化可讀字節:13
執行markReaderIndex

讀取兩個字節

讀取后可讀字節:11
執行resetReaderIndex后可讀字節:13
執行clear后可讀字節:0
Exception in thread "main" java.lang.IndexOutOfBoundsException: readerIndex(0) + length(2) exceeds writerIndex(0): UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeNoCleanerDirectByteBuf(ridx: 0, widx: 0, cap: 100)
    at io.netty.buffer.AbstractByteBuf.checkReadableBytes0(AbstractByteBuf.java:1403)
    at io.netty.buffer.AbstractByteBuf.checkReadableBytes(AbstractByteBuf.java:1390)
    at io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:843)
    at com.eric.bytebuf.OperationSample.indexManage(OperationSample.java:32)
    at com.eric.bytebuf.OperationSample.main(OperationSample.java:16)
派生緩沖區

派生緩沖區為 ByteBuf 提供了以專門的方式來呈現其內容的視圖。這類視圖是通過以下方法被創建的:

duplicate()
slice()
slice(int, int)
Unpooled.unmodifiableBuffer(…)
order(ByteOrder)
readSlice(int)

上述這些方法都將返回一個新的 ByteBuf 實例, 它具有自己的讀索引、寫索引和標記 索引.

但是其內部存儲和原始對象是共享的. 該種方式創建成本很低廉, 但是這也意味著, 如果你修改了它的內容, 也同時修改了其對應的源實例, 所以要小心.

如果需要一個現有緩沖區的真實副本, 請使用 copy() 或者 copy(int, int) 方法. 不同于派生緩沖區, 由這個調用所返回的 ByteBuf 擁有獨立的數據副本.

        Charset utf8 = Charset.forName("UTF-8");
        ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8);
        ByteBuf slice = buf.slice(0, 15);
        System.out.println(slice.toString(utf8));
        buf.setByte(0,"J");
        assert buf.getByte(0) == slice.getByte(0);
讀/寫操作

正如前文所提到過的, 有兩種類別的讀/寫操作:

get()set() 操作, 從給定的索引開始,并且保持索引不變;

read()write() 操作, 從給定的索引開始,并且會根據已經訪問過的字節數對索引進行調整.

ByteBufHolder

我們經常發現, 除了實際的數據負載之外, 我們還需要存儲各種屬性值. HTTP 響應便是一個很好的例子, 除了表示為字節的內容, 還包括狀態碼、cookie 等.

為了處理這種常見的用例, Netty 提供了 ByteBufHolder. ByteBufHolder 也為 Netty 的高級特性提供了支持, 如緩沖區池化, 其中可以從池中借用 ByteBuf, 并且在需要時自動釋放.

ByteBufHolder 只有幾種用于訪問底層數據和引用計數的方法:

content(): 返回由這個 ByteBufHolder 所持有的 ByteBuf.

copy(): 返回這個 ByteBufHolder 的一個深拷貝, 包括一個其所包含的 ByteBuf 的非共享拷貝.

duplicate(): 返回這個 ByteBufHolder 的一個淺拷貝, 包括一個其所包含的 ByteBuf 的共享拷貝.

系統默認自帶了一系列的 ByteBufHolder, 以 MemoryFileUpload 為例, 該類通過封裝將 filename, contentType, contentTransferEncoding 屬性與對應的file進行關聯.

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/74269.html

相關文章

  • Netty ByteBuf 誰負責誰釋放

    摘要:轉發自 轉發自 http://netty.io/wiki/referenc... Since Netty version 4, the life cycle of certain objects are managed by their reference counts, so that Netty can return them (or their shared resources)...

    Lyux 評論0 收藏0
  • 對于 Netty ByteBuf 的零拷貝(Zero Copy) 的理解

    摘要:根據對的定義即所謂的就是在操作數據時不需要將數據從一個內存區域拷貝到另一個內存區域因為少了一次內存的拷貝因此的效率就得到的提升在層面上的通常指避免在用戶態與內核態之間來回拷貝數據例如提供的系統調用它可以將一段用戶空間內存映射到內 根據 Wiki 對 Zero-copy 的定義: Zero-copy describes computer operations in which the C...

    ConardLi 評論0 收藏0
  • Netty入門學習-ByteBuf

    摘要:使用來優化套接字操作,盡可能消除由的緩沖區實現所導致的性能以及內存使用率的懲罰,這種優化發生在的核心代碼中,不會被暴露出來。當前將會被增加所寫入的字節數。 ByteBuf是Java NIO ByteBuffer的替代品,是網絡數據基本單位字節的容器。 ByteBuf的API Netty的數據處理API通過兩個組件暴漏:抽象類ByteBuf和接口ByteBufHolder ByteBuf...

    beanlam 評論0 收藏0
  • netty實戰》閱讀筆記(2)——Netty 的數據容器ByteBuf

    摘要:當你從讀取時,它的將會被遞增已經被讀取的字節數。達到和位于同一位置,表示我們到達可以讀取的數據的末尾。該應用程序可以選擇為多個消息重用相同的消息主體。 ByteBuffer 當我們進行數據傳輸的時候,往往需要使用到緩沖區,常用的緩沖區就是JDK NIO類庫提供的java.nio.Buffer。 showImg(https://segmentfault.com/img/bVbbz8p?w...

    huangjinnan 評論0 收藏0
  • Netty ByteBuf

    摘要:主要用來檢測對象是否泄漏。子類實現相關的方法是否支持數組,判斷緩沖區的實現是否基于字節數組如果緩沖區的實現基于字節數組,返回字節數組 ByteBuf ByteBuf需要提供JDK ByteBuffer的功能(包含且不限于),主要有以下幾類基本功能: 7種Java基礎類型、byte[]、ByteBuffer(ByteBuf)的等的讀寫 緩沖區自身的copy和slice 設置網絡字節序 ...

    meislzhua 評論0 收藏0

發表評論

0條評論

whataa

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<