1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        【48期】盤點Netty面試常問考點:什么是 Netty 的零拷貝?

        共 2459字,需瀏覽 5分鐘

         ·

        2020-09-26 21:56

        程序員的成長之路
        互聯(lián)網(wǎng)/程序員/技術/資料共享?
        關注


        閱讀本文大概需要 4.5 分鐘。

        來自:my.oschina.net/plucury/blog/192577

        理解零拷貝 零拷貝是Netty的重要特性之一,而究竟什么是零拷貝呢?WIKI中對其有如下定義:
        "Zero-copy" describes computer operations in which the CPU does not perform the task of copying data from one memory area to another.
        從WIKI的定義中,我們看到“零拷貝”是指計算機操作的過程中,CPU不需要為數(shù)據(jù)在內存之間的拷貝消耗資源。而它通常是指計算機在網(wǎng)絡上發(fā)送文件時,不需要將文件內容拷貝到用戶空間(User Space)而直接在內核空間(Kernel Space)中傳輸?shù)骄W(wǎng)絡的方式。
        Non-Zero Copy方式:
        Zero Copy方式:
        從上圖中可以清楚的看到,Zero Copy的模式中,避免了數(shù)據(jù)在用戶空間和內存空間之間的拷貝,從而提高了系統(tǒng)的整體性能。Linux中的sendfile()以及Java NIO中的FileChannel.transferTo()方法都實現(xiàn)了零拷貝的功能,而在Netty中也通過在FileRegion中包裝了NIO的FileChannel.transferTo()方法實現(xiàn)了零拷貝。
        而在Netty中還有另一種形式的零拷貝,即Netty允許我們將多段數(shù)據(jù)合并為一整段虛擬數(shù)據(jù)供用戶使用,而過程中不需要對數(shù)據(jù)進行拷貝操作,這也是我們今天要講的重點。我們都知道在stream-based transport(如TCP/IP)的傳輸過程中,數(shù)據(jù)包有可能會被重新封裝在不同的數(shù)據(jù)包中,例如當你發(fā)送如下數(shù)據(jù)時:
        有可能實際收到的數(shù)據(jù)如下:
        因此在實際應用中,很有可能一條完整的消息被分割為多個數(shù)據(jù)包進行網(wǎng)絡傳輸,而單個的數(shù)據(jù)包對你而言是沒有意義的,只有當這些數(shù)據(jù)包組成一條完整的消息時你才能做出正確的處理,而Netty可以通過零拷貝的方式將這些數(shù)據(jù)包組合成一條完整的消息供你來使用。而此時,零拷貝的作用范圍僅在用戶空間中。
        以Netty 3.8.0.Final的源代碼來進行說明 ###ChannelBuffer接口 Netty為需要傳輸?shù)臄?shù)據(jù)制定了統(tǒng)一的ChannelBuffer接口。該接口的主要設計思路如下:
        1.使用getByte(int index)方法來實現(xiàn)隨機訪問
        2.使用雙指針的方式實現(xiàn)順序訪問
        每個Buffer都有一個讀指針(readIndex)和寫指針(writeIndex)
        在讀取數(shù)據(jù)時讀指針后移,在寫入數(shù)據(jù)時寫指針后移
        定義了統(tǒng)一的接口之后,就是來做各種實現(xiàn)了。Netty主要實現(xiàn)了HeapChannelBuffer,ByteBufferBackedChannelBuffer等等,下面我們就來講講與Zero Copy直接相關的CompositeChannelBuffer類。###CompositeChannelBuffer類 CompositeChannelBuffer類的作用是將多個ChannelBuffer組成一個虛擬的ChannelBuffer來進行操作。
        為什么說是虛擬的呢,因為CompositeChannelBuffer并沒有將多個ChannelBuffer真正的組合起來,而只是保存了他們的引用,這樣就避免了數(shù)據(jù)的拷貝,實現(xiàn)了Zero Copy。下面我們來看看具體的代碼實現(xiàn),首先是成員變量
        private?int?readerIndex;
        private?int?writerIndex;
        private?ChannelBuffer[]?components;
        private?int[]?indices;
        private?int?lastAccessedComponentId;
        以上這里列出了幾個比較重要的成員變量。其中readerIndex既讀指針和writerIndex既寫指針是從AbstractChannelBuffer繼承而來的;然后components是一個ChannelBuffer的數(shù)組,他保存了組成這個虛擬Buffer的所有子Buffer,indices是一個int類型的數(shù)組,它保存的是各個Buffer的索引值;最后的lastAccessedComponentId是一個int值,它記錄了最后一次訪問時的子Buffer ID。
        從這個數(shù)據(jù)結構,我們不難發(fā)現(xiàn)所謂的CompositeChannelBuffer實際上就是將一系列的Buffer通過數(shù)組保存起來,然后實現(xiàn)了ChannelBuffer 的接口,使得在上層看來,操作這些Buffer就像是操作一個單獨的Buffer一樣。
        創(chuàng)建 接下來,我們再看一下CompositeChannelBuffer.setComponents方法,它會在初始化CompositeChannelBuffer時被調用。
        /**
        ?*?Setup?this?ChannelBuffer?from?the?list
        ?*/

        private?void?setComponents(List?newComponents)?{
        ????assert?!newComponents.isEmpty();

        ????//?Clear?the?cache.
        ????lastAccessedComponentId?=?0;

        ????//?Build?the?component?array.
        ????components?=?new?ChannelBuffer[newComponents.size()];
        ????for?(int?i?=?0;?i?????????ChannelBuffer?c?=?newComponents.get(i);
        ????????if?(c.order()?!=?order())?{
        ????????????throw?new?IllegalArgumentException(
        ????????????????????"All?buffers?must?have?the?same?endianness.");
        ????????}

        ????????assert?c.readerIndex()?==?0;
        ????????assert?c.writerIndex()?==?c.capacity();

        ????????components[i]?=?c;
        ????}

        ????//?Build?the?component?lookup?table.
        ????indices?=?new?int[components.length?+?1];
        ????indices[0]?=?0;
        ????for?(int?i?=?1;?i?<=?components.length;?i?++)?{
        ????????indices[i]?=?indices[i?-?1]?+?components[i?-?1].capacity();
        ????}

        ????//?Reset?the?indexes.
        ????setIndex(0,?capacity());
        }
        通過代碼可以看到該方法的功能就是將一個ChannelBuffer的List給組合起來。它首先將List中得元素放入到components數(shù)組中,然后創(chuàng)建indices用于數(shù)據(jù)的查找,最后使用setIndex來重置指針。這里需要注意的是setIndex(0, capacity())會將讀指針設置為0,寫指針設置為當前Buffer的長度,這也就是前面需要做assert c.readerIndex() == 0和assert c.writerIndex() == c.capacity()這兩個判斷的原因,否則很容易會造成數(shù)據(jù)重復讀寫的問題。
        所以Netty推薦我們使用ChannelBuffers.wrappedBuffer方法來進行Buffer的合并,因為在該方法中Netty會通過slice()方法來確保構建CompositeChannelBuffer是傳入的所有子Buffer都是符合要求的。
        數(shù)據(jù)訪問 CompositeChannelBuffer.getByte(int index)的實現(xiàn)如下:
        public?byte?getByte(int?index)?{
        ????int?componentId?=?componentId(index);
        ????return?components[componentId].getByte(index?-?indices[componentId]);
        }
        從代碼我們可以看到,在隨機查找時會首先通過index獲取這個字節(jié)所在的componentId既字節(jié)所在的子Buffer序列,然后通過index - indices[componentId]計算出它在這個子Buffer中的第幾個字節(jié),然后返回結果。
        下面再來看一下componentId(int index) 的實現(xiàn):
        private?int?componentId(int?index)?{
        ????int?lastComponentId?=?lastAccessedComponentId;
        ????if?(index?>=?indices[lastComponentId])?{
        ????????if?(index?1])?{
        ????????????return?lastComponentId;
        ????????}

        ????????//?Search?right
        ????????for?(int?i?=?lastComponentId?+?1;?i?????????????if?(index?1])?{
        ????????????????lastAccessedComponentId?=?i;
        ????????????????return?i;
        ????????????}
        ????????}
        ????}?else?{
        ????????//?Search?left
        ????????for?(int?i?=?lastComponentId?-?1;?i?>=?0;?i?--)?{
        ????????????if?(index?>=?indices[i])?{
        ????????????????lastAccessedComponentId?=?i;
        ????????????????return?i;
        ????????????}
        ????????}
        ????}

        ????throw?new?IndexOutOfBoundsException("Invalid?index:?"?+?index?+?",?maximum:?"?+?indices.length);
        }
        從代碼中我們發(fā)現(xiàn),Netty以lastComponentId既上次訪問的子Buffer序號為中心,向左右兩邊進行搜索,這樣做的目的是,當我們兩次隨機查找的字符序列相近時(大部分情況下都是這樣),可以最快的搜索到目標索引的componentId。

        推薦閱讀:

        【47期】六大類二叉樹面試題匯總解答

        【46期】盤點那些必問的數(shù)據(jù)結構算法題之快速排序

        【45期】盤點那些必問的數(shù)據(jù)結構算法題之基礎排序

        5T技術資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機,樹莓派,等等。在公眾號內回復「2048」,即可免費獲取??!

        微信掃描二維碼,關注我的公眾號

        朕已閱?

        瀏覽 40
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            成人靠逼 | 午夜成人无码精品免费 | 国产精品久久久久久无码日本蜜乳 | 国产亚洲AV片天天在线观看 | 日本舔阴视频 | 久草资源在线 | 国产成人无码视频在线观看原 | 天天干夜夜操911 | 懂色av粉嫩av色欲av | 国产片婬乱一级毛片影片乱叫 |