• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • Netty拾遺(六)——Netty中的ByteBuf

    標簽: # Netty  netty

    前言

    我們之前在介紹NIO的三個組件的時候,總結過NIO中的ByteBuffer,針對NIO中的ByteBuffer,我們在讀寫的時候,需要調用flip等方法做一個顯示的讀寫模式的切換。在使用上有些許的不方便。Netty為了更好的讀取和寫入數據,單獨封裝了一個ByteBuf。

    ByteBuf

    ByteBuf數據結構

    ByteBuf內部是一個字節的容器,內部其實就是一個字節數組,但整體也可分為四個部分

    第一個部分:已用字節,已被廢棄的無效字節。

    第二個部分:可讀字節,從ByteBuf中讀取的字節都來自這個部分。

    第三個部分:可寫字節,寫入都ByteBuf的直接都會寫到這個部分。

    第四個部分:可擴容字節,最多還能擴容的大小。

    在這里插入圖片描述

    ByteBuf的重要屬性

    前面提到過,ByteBuf的在讀寫的時候不需要進行讀寫模式的切換,主要是因為ByteBuf通過三個屬性有效的區分可讀數據和可寫數據。

    readerIndex 讀指針,每次讀取一個字節,readerIndex自動增加,當readerIndex等于writerIndex的時候,表示ByteBuf不可讀了。并不需要我們切換。

    writerIndex寫指針,表示寫入的起始位置,每寫一個自己,writerIndex自動增加一位,一旦writerIndex與capacity()容量相等,表示ByteBuf已經不可寫了。

    maxCapacity最大容量,表示ByteBuf可以擴容的最大容量,如果超過maxCapacity則會報錯。

    直接看圖吧

    在這里插入圖片描述

    ByteBuf的引用計數

    Netty中的ByteBuf的內存回收工作是通過引用計數的管理方式(就是JVM中使用的“計數器”)來標記對象是否"不可達",如果不可達,則會進行內存回收。默認情況下創建完一個ByteBuf的時候,這個ByteBuf的引用為1,每次調用retain方法,這個ByteBuf的計數就增加1,每次調用release方法,則ByteBuf的引用計數就會減一。如果引用計數減為0,則再次訪問這個ByteBuf對象就會拋出異常,因為這個ByteBuf就會被回收。

    ByteBuf的分配器

    Netty通過ByteBufAllocator分配器來創建和分配緩沖內存空間。其中有池化的分配器——PoolByteBufAllocator和非池化的分配器——UnpooledByteBufAllocator 前者將分配的ByteBuf放入池中,后者每次產生新的ByteBuf,通過Java的垃圾回收機制回收。總體來看ByteBuf的分配有以下幾種方式

    //使用默認的分配方式分配ByteBuf
    buffer = ByteBufAllocator.DEFAULT.buffer(9, 100);
    
    //利用非池化分配方式,分配ByteBuf
    buffer = UnpooledByteBufAllocator.DEFAULT.buffer();
    
    //利用池化分配方式,分配堆外ByteBuf
    buffer = PooledByteBufAllocator.DEFAULT.directBuffer();
    
    //利用池化分配方式,分配堆ByteBuf
    buffer = PooledByteBufAllocator.DEFAULT.heapBuffer();
    

    如果沒有特別要求,其實第一種分配方式就可以了。

    ByteBuf的類型

    ByteBuf的類型其實不止一個,主要有以下三個。

    類型說明
    Heap ByteBuf存儲在Java堆中,通過hasArray來判斷是不是堆緩沖區
    Direct ByteBuf直接緩沖區,直接在操作系統的物理內存中分配
    CompositeBuffer多個緩沖區的組合表示

    上述的三種,都可以通過池化和非池化的方式來進行分配。這里重點說一下Heap ByteBuf和Direct ByteBuf,后者并不分配于Java堆內存,前者是分配于Java堆上。兩者在讀取數據時是還是有區別的。

    HeapByteBuf :特點是內存的分配和回收速度快。可以被jvm自動回收。缺點就是在進行socket的I/O讀寫時,需要將堆內存的緩沖區拷貝到內核中,有一定拷貝的代價。
    DirectByteBuf:特點是分配在堆內存外,相比于堆內存分配速度會慢一點,并需要手動管理其引用計數,清理不使用的內存。但是其直接用native方法,不需要拷貝。

    ByteBuf的釋放

    除了我們顯示調用ByteBuf的分配,通過查看Netty源碼,會發現Netty的相關線程,會在底層的NIO通道讀取數據的時候,調用ByteBufAllocator方法,創建ByteBuf實例,然后調用pipeline.fireChannelRead(byteBuf)方法將讀取到的數據包送入到入站處理流水線中

    針對ByteBuf的釋放,Netty有幾種方式

    如果我們的處理器繼承至ChannelInboundHandlerAdapter則除了手動釋放ByteBuf之外,在調用父類的read方法時,會自動完成ByteBuf的釋放。

    如果繼承至SimpleChannelInboundContext除了手動釋放ByteBuf之外,會自動釋放ByteBuf。從源碼中可以看出

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        boolean release = true;
        try {
            if (acceptInboundMessage(msg)) {
                @SuppressWarnings("unchecked")
                I imsg = (I) msg;
                channelRead0(ctx, imsg);//子類的channelRead方法
            } else {
                release = false;
                ctx.fireChannelRead(msg);
            }
        } finally {
            if (autoRelease && release) {
                ReferenceCountUtil.release(msg);//這里會自動釋放
            }
        }
    }
    

    實例

    基本的簡單讀寫

    /**
     * autor:liman
     * createtime:2020/9/19
     * comment:基本的ByteBuf讀寫
     */
    @Slf4j
    public class BaseReadAndWriteBuf {
    
        public static void main(String[] args) {
            ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(9,100);
            log.info("分配初始化9,最大空間100的buffer,{}",buffer);
            byte[] data = new byte[]{1,2,3,4};
            log.info("開始寫入數據");
            writeData(buffer,data);
            log.info("讀數據");
            readDataFromBuffer(buffer);
            log.info("取數據");
            getByteFromBuffer(buffer);
            log.info("all done!");
        }
    
        public static void writeData(ByteBuf buffer,byte[] dataByte){
            buffer.writeBytes(dataByte);
        }
    
        public static void readDataFromBuffer(ByteBuf buffer){
            while(buffer.isReadable()){
                log.info("讀一個字節:{}",buffer.readByte());
            }
        }
    
        public static void getByteFromBuffer(ByteBuf buffer){
            for(int i=0;i<buffer.readableBytes();i++){
                log.info("獲取一個字節:{}",buffer.getByte(i));
            }
        }
    }
    

    三種ByteBuf實例

    直接上實例代碼

    /**
     * autor:liman
     * createtime:2020/9/19
     * comment:三種ByteBuf的種類介紹
     */
    @Slf4j
    public class ByteBufTypeDemo {
    
        private static final Charset UTF8_CharSet = Charset.forName("UTF-8");
    
        public static void main(String[] args) {
            testHeapByteBuf();
            testDirectByteBuf();
            testCompositeBuffer();
        }
    
        /**
         * 測試Heaper Buffer
         */
        public static void testHeapByteBuf() {
            ByteBuf heapBuf = ByteBufAllocator.DEFAULT.heapBuffer();
            heapBuf.writeBytes("Heap Buf的簡單實例".getBytes(UTF8_CharSet));
            if (heapBuf.hasArray()) {
                byte[] array = heapBuf.array();
                int offset = heapBuf.arrayOffset() + heapBuf.readerIndex();
                int length = heapBuf.readableBytes();
                String s = new String(array, offset, length, UTF8_CharSet);
                log.info("Heap Buf 讀取結果為:{}", s);
            }
            heapBuf.release();
        }
    
        /**
         * 測試的directBuffer
         */
        public static void testDirectByteBuf() {
            ByteBuf directBuf = ByteBufAllocator.DEFAULT.directBuffer();
            directBuf.writeBytes("direct Buf的簡單實例".getBytes(UTF8_CharSet));
            if (!directBuf.hasArray()) {
                int length = directBuf.readableBytes();
                byte[] array = new byte[length];
                directBuf.getBytes(directBuf.readerIndex(), array);
                String content = new String(array, UTF8_CharSet);
                log.info("direct buf 讀取的數據為:{}", content);
            }
            directBuf.release();
        }
    
        /**
         * 測試組合緩沖區
         */
        public static void testCompositeBuffer() {
            CompositeByteBuf compositeByteBuf = ByteBufAllocator.DEFAULT.compositeBuffer();
            //這個底層會調用并分配一個堆緩沖區
            ByteBuf headerBuf = Unpooled.copiedBuffer("組合緩沖區的header內容", UTF8_CharSet);
            ByteBuf bodyBuf = Unpooled.copiedBuffer("組合緩沖區的body部分", UTF8_CharSet);
            compositeByteBuf.addComponents(headerBuf, bodyBuf);
            readCompositeBufferInfo(compositeByteBuf);
        }
    
        /**
         * 讀取compositeBuffer中的內容
         *
         * @param compositeByteBuf
         */
        public static void readCompositeBufferInfo(CompositeByteBuf compositeByteBuf) {
            String result = "";
            for (ByteBuf byteBuf : compositeByteBuf) {
                int length = byteBuf.readableBytes();
                byte[] array = new byte[length];
                byteBuf.getBytes(byteBuf.readerIndex(), array);
                result = new String(array, UTF8_CharSet);
                log.info("composite buffer讀取結果:{}", result);
            }
        }
    }
    

    總結

    簡單總結了ByteBuf中的內容,后續總結Netty的相關解碼器

    版權聲明:本文為liman65727原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/liman65727/article/details/108684168

    智能推薦

    HTML拾遺

    目錄 一 鏈接標簽 二 行內元素和塊元素 三 視頻和音頻 四 頁面結構分析 五 iframe內聯框架 一 鏈接標簽 1 點睛 <a href="path" target="目標窗口位置"></a> path:鏈接路徑 target:鏈接在哪個窗口打開。常用值:_se...

    拾遺增補

    拾遺增補 1.1 線程的狀態 狀態 描述 NEW 尚未啟動的線程處于這種狀態 RUNNABLE 在Java虛擬機中正在執行的線程處于此狀態 BLOCKED 補阻塞等待對象鎖的線程處于此狀態 WAITING 正在等待另一個線程執行特定動作的線程處于h此x狀態 TIME_WAITING 正在等待另一個線程執行達到指定的等待時間的線程處于此狀態 TERMINATED 已經退出運行的線程處于此狀態 如圖所...

    Java網絡編程--Netty中的ByteBuf

    由于JDK中提供的ByteBuffer無法動態擴容,并且API使用復雜等原因,Netty中提供了ByteBuf。 Bytebuf的API操作更加便捷,可以動態擴容,提供了多種ByteBuf的實現,以及高效的零拷貝機制。 ByteBuf的操作 ByteBuf有三個重要的屬性:capacity容量,readerIndex讀取位置,writerIndex寫入位置 提供了readerIndex和weite...

    netty(六) ByteBuf學習.md

    ByteBuf學習 基于Java Nio的網絡通信,必須依賴于ByteBuffer,從程序實際上是借助于ByteBuffer來與Channel進行交互的,可以向Channel讀寫數據,但是Java Nio原生的ByteBuffer存在一些問題。 ByteBuffer有兩種模式:讀模式和寫模式,寫模式切換到讀模式的時候,必須顯示地調用flip()方法,否則從緩沖區中讀數據會出現異常。這就要求程序猿必...

    Netty的ByteBuf

    轉自:https://blog.csdn.net/yjw123456/article/details/77843931 正如之前所說,網絡傳輸的基本單位是字節。Java NIO 提供了ByteBuffer作為它的容器,但是這個類使用起來比較復雜和麻煩。Netty提供了一個更好的實現:ByteBuf。 ByteBuf的API Netty為數據處理提供的API通過抽象類ByteBuf和接口ByteBu...

    猜你喜歡

    數據結構拾遺(2) --紅黑樹的設計與實現(中)

    Insert完善     根據規則4, 新增節點必須為紅; 根據規則3, 新增節點之父節點必須為黑.   示例:     (1)插入16(紅色)/55(紅色), 則既不用旋轉, 也不用重新染色     (2)插入82(紅色), 則違反了紅黑規則, 需要進行動態...

    【筆記】天梯賽拾遺(1)——c++中find的五個用法

    代碼 運行結果 find函數在做題中的實踐 例一:2020天梯賽 L1-6 吃火鍋 例二:給出一個字符串,串中會出現有人名,找到一個只有一個人名的字符串。 例三:你有n個字符串。 每個字符串由小寫英文字母組成。 重新排序給定的字符串,使得對于每個字符串,在它之前的所有字符串都是它的子串。 例四:查詢區間內子串在母串中的個數。 參考:https://www.cnblogs.com/wkfvawl/p...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    精品国产乱码久久久久久蜜桃不卡