如何理解Netty中的零拷貝機(jī)制

本篇內(nèi)容介紹了“如何理解Netty中的零拷貝機(jī)制”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

10年積累的成都網(wǎng)站建設(shè)、做網(wǎng)站經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站設(shè)計(jì)后付款的網(wǎng)站建設(shè)流程,更有龍游免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。

理解零拷貝

零拷貝是Netty的重要特性之一,而究竟什么是零拷貝呢? 
WIKI中對(duì)其有如下定義:

“Zero-copy” describes computer operations in which the CPU does not perform the task of copying data from one memory area to another.

從WIKI的定義中,我們看到“零拷貝”是指計(jì)算機(jī)操作的過程中,CPU不需要為數(shù)據(jù)在內(nèi)存之間的拷貝消耗資源。而它通常是指計(jì)算機(jī)在網(wǎng)絡(luò)上發(fā)送文件時(shí),不需要將文件內(nèi)容拷貝到用戶空間(User Space)而直接在內(nèi)核空間(Kernel Space)中傳輸?shù)骄W(wǎng)絡(luò)的方式。

Non-Zero Copy方式: 
如何理解Netty中的零拷貝機(jī)制

Zero Copy的模式中,避免了數(shù)據(jù)在用戶空間和內(nèi)存空間之間的拷貝,從而提高了系統(tǒng)的整體性能。Linux中的sendfile()以及Java NIO中的FileChannel.transferTo()方法都實(shí)現(xiàn)了零拷貝的功能,而在Netty中也通過在FileRegion中包裝了NIO的FileChannel.transferTo()方法實(shí)現(xiàn)了零拷貝。

而在Netty中還有另一種形式的零拷貝,即Netty允許我們將多段數(shù)據(jù)合并為一整段虛擬數(shù)據(jù)供用戶使用,而過程中不需要對(duì)數(shù)據(jù)進(jìn)行拷貝操作,這也是我們今天要講的重點(diǎn)。我們都知道在stream-based transport(如TCP/IP)的傳輸過程中,數(shù)據(jù)包有可能會(huì)被重新封裝在不同的數(shù)據(jù)包中,例如當(dāng)你發(fā)送如下數(shù)據(jù)時(shí):

如何理解Netty中的零拷貝機(jī)制

有可能實(shí)際收到的數(shù)據(jù)不完整

因此在實(shí)際應(yīng)用中,很有可能一條完整的消息被分割為多個(gè)數(shù)據(jù)包進(jìn)行網(wǎng)絡(luò)傳輸,而單個(gè)的數(shù)據(jù)包對(duì)你而言是沒有意義的,只有當(dāng)這些數(shù)據(jù)包組成一條完整的消息時(shí)你才能做出正確的處理,而Netty可以通過零拷貝的方式將這些數(shù)據(jù)包組合成一條完整的消息供你來使用。而此時(shí),零拷貝的作用范圍僅在用戶空間中。

如何理解Netty中的零拷貝機(jī)制

Netty3中零拷貝的實(shí)現(xiàn)機(jī)制

以下以Netty 3.8.0.Final的源代碼來進(jìn)行說明

ChannelBuffer接口

Netty為需要傳輸?shù)臄?shù)據(jù)制定了統(tǒng)一的ChannelBuffer接口。該接口的主要設(shè)計(jì)思路如下:

  • 使用getByte(int index)方法來實(shí)現(xiàn)隨機(jī)訪問

  • 使用雙指針的方式實(shí)現(xiàn)順序訪問

    • 每個(gè)Buffer都有一個(gè)讀指針(readIndex)和寫指針(writeIndex)

    • 在讀取數(shù)據(jù)時(shí)讀指針后移,在寫入數(shù)據(jù)時(shí)寫指針后移 
      在此輸入圖片描述

定義了統(tǒng)一的接口之后,就是來做各種實(shí)現(xiàn)了。Netty主要實(shí)現(xiàn)了HeapChannelBuffer,ByteBufferBackedChannelBuffer等等,下面我們就來講講與Zero Copy直接相關(guān)的CompositeChannelBuffer類。

CompositeChannelBuffer類

CompositeChannelBuffer類的作用是將多個(gè)ChannelBuffer組成一個(gè)虛擬的ChannelBuffer來進(jìn)行操作。為什么說是虛擬的呢,因?yàn)?code>CompositeChannelBuffer并沒有將多個(gè)ChannelBuffer真正的組合起來,而只是保存了他們的引用,這樣就避免了數(shù)據(jù)的拷貝,實(shí)現(xiàn)了Zero Copy。 
下面我們來看看具體的代碼實(shí)現(xiàn),首先是成員變量

private int readerIndex;
private int writerIndex;
private ChannelBuffer[] components;
private int[] indices;
private int lastAccessedComponentId;

以上這里列出了幾個(gè)比較重要的成員變量。其中readerIndex既讀指針和writerIndex既寫指針是從AbstractChannelBuffer繼承而來的;然后components是一個(gè)ChannelBuffer的數(shù)組,他保存了組成這個(gè)虛擬Buffer的所有子Buffer,indices是一個(gè)int類型的數(shù)組,它保存的是各個(gè)Buffer的索引值;最后的lastAccessedComponentId是一個(gè)int值,它記錄了最后一次訪問時(shí)的子Buffer ID。從這個(gè)數(shù)據(jù)結(jié)構(gòu),我們不難發(fā)現(xiàn)所謂的CompositeChannelBuffer實(shí)際上就是將一系列的Buffer通過數(shù)組保存起來,然后實(shí)現(xiàn)了ChannelBuffer 的接口,使得在上層看來,操作這些Buffer就像是操作一個(gè)單獨(dú)的Buffer一樣。

創(chuàng)建

接下來,我們?cè)倏匆幌?code>CompositeChannelBuffer.setComponents方法,它會(huì)在初始化CompositeChannelBuffer時(shí)被調(diào)用。

/**
 * Setup this ChannelBuffer from the list
 */
private void setComponents(List<ChannelBuffer> newComponents) {
    assert !newComponents.isEmpty();

    // Clear the cache.
    lastAccessedComponentId = 0;

    // Build the component array.
    components = new ChannelBuffer[newComponents.size()];
    for (int i = 0; i < components.length; 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());
}

通過代碼可以看到該方法的功能就是將一個(gè)ChannelBuffer的List給組合起來。它首先將List中得元素放入到components數(shù)組中,然后創(chuàng)建indices用于數(shù)據(jù)的查找,最后使用setIndex來重置指針。這里需要注意的是setIndex(0, capacity())會(huì)將讀指針設(shè)置為0,寫指針設(shè)置為當(dāng)前Buffer的長度,這也就是前面需要做assert c.readerIndex() == 0assert c.writerIndex() == c.capacity()這兩個(gè)判斷的原因,否則很容易會(huì)造成數(shù)據(jù)重復(fù)讀寫的問題,所以Netty推薦我們使用ChannelBuffers.wrappedBuffer方法來進(jìn)行Buffer的合并,因?yàn)樵谠摲椒ㄖ蠳etty會(huì)通過slice()方法來確保構(gòu)建CompositeChannelBuffer是傳入的所有子Buffer都是符合要求的。

數(shù)據(jù)訪問

CompositeChannelBuffer.getByte(int index)的實(shí)現(xiàn)如下:

public byte getByte(int index) {
    int componentId = componentId(index);
    return components[componentId].getByte(index - indices[componentId]);
}

從代碼我們可以看到,在隨機(jī)查找時(shí)會(huì)首先通過index獲取這個(gè)字節(jié)所在的componentId既字節(jié)所在的子Buffer序列,然后通過index - indices[componentId]計(jì)算出它在這個(gè)子Buffer中的第幾個(gè)字節(jié),然后返回結(jié)果。

下面再來看一下componentId(int index)的實(shí)現(xiàn):

private int componentId(int index) {
    int lastComponentId = lastAccessedComponentId;
    if (index >= indices[lastComponentId]) {
        if (index < indices[lastComponentId + 1]) {
            return lastComponentId;
        }

        // Search right
        for (int i = lastComponentId + 1; i < components.length; i ++) {
            if (index < indices[i + 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序號(hào)為中心,向左右兩邊進(jìn)行搜索,這樣做的目的是,當(dāng)我們兩次隨機(jī)查找的字符序列相近時(shí)(大部分情況下都是這樣),可以最快的搜索到目標(biāo)索引的componentId。

“如何理解Netty中的零拷貝機(jī)制”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

分享名稱:如何理解Netty中的零拷貝機(jī)制
本文網(wǎng)址:http://muchs.cn/article36/pidgpg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護(hù)、自適應(yīng)網(wǎng)站網(wǎng)站營銷、軟件開發(fā)、網(wǎng)站設(shè)計(jì)App設(shè)計(jì)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都做網(wǎng)站