亚洲全黄无码一级在线看_国产剧情久久久性色_无码av一区二区三区无码_亚洲成a×人片在线观看

當(dāng)前位置: 首頁(yè) > 科技新聞 >

Kafka是如何利用零拷貝提高性能的

時(shí)間:2020-06-18 17:35來(lái)源:網(wǎng)絡(luò)整理 瀏覽:
Kafka 在執(zhí)行消息的寫(xiě)入和讀取這么快的原因,其中的一個(gè)原因是零拷貝(Zero-copy)技術(shù),下面我們來(lái)了解一下這么高效的原因。傳統(tǒng)的文

Kafka 在執(zhí)行消息的寫(xiě)入和讀取這么快的原因,其中的一個(gè)原因是零拷貝(Zero-copy)技術(shù),下面我們來(lái)了解一下這么高效的原因。

傳統(tǒng)的文件讀寫(xiě)

傳統(tǒng)的文件讀寫(xiě)或者網(wǎng)絡(luò)傳輸,通常需要將數(shù)據(jù)從 內(nèi)核態(tài) 轉(zhuǎn)換為 用戶(hù)態(tài) 。應(yīng)用程序讀取用戶(hù)態(tài)內(nèi)存數(shù)據(jù),寫(xiě)入文件 / Socket之前,需要從用戶(hù)態(tài)轉(zhuǎn)換為內(nèi)核態(tài)之后才可以寫(xiě)入文件或者網(wǎng)卡當(dāng)中。

Kafka是如何利用零拷貝提高性能的


數(shù)據(jù)首先從磁盤(pán)讀取到內(nèi)核緩沖區(qū),這里面的內(nèi)核緩沖區(qū)就是頁(yè)緩存(PageCache)。然后從內(nèi)核緩沖區(qū)中復(fù)制到應(yīng)用程序緩沖區(qū)(用戶(hù)態(tài)),輸出到輸出設(shè)備時(shí),又會(huì)將用戶(hù)態(tài)數(shù)據(jù)轉(zhuǎn)換為內(nèi)核態(tài)數(shù)據(jù)。

DMA

在介紹零拷貝之前,我們先來(lái)看一個(gè)技術(shù)名詞DMA(Direct Memory Access 直接內(nèi)存訪問(wèn))。它是現(xiàn)代電腦的重要特征之一,允許不同速度的硬件之間直接交互,而不需要占用CPU的中斷負(fù)載。DMA傳輸將一個(gè)地址空間復(fù)制到另一個(gè)地址空間,當(dāng)CPU 初始化這個(gè)傳輸之后,實(shí)際的數(shù)據(jù)傳輸是有DMA設(shè)備之間完成,這樣可以大大的減少CPU的消耗。我們常見(jiàn)的硬件設(shè)備都支持DMA,如下圖所示:

Kafka是如何利用零拷貝提高性能的


零拷貝

對(duì)于常見(jiàn)的零拷貝,我們下面主要介紹一下 mmapsendfile 兩種方式。下面的介紹我們基于磁盤(pán)文件拷貝的方式去講解。

mmap

mmap 就是在用戶(hù)態(tài)直接引用文件句柄,也就是用戶(hù)態(tài)和內(nèi)核態(tài)共享內(nèi)核態(tài)的數(shù)據(jù)緩沖區(qū),此時(shí)數(shù)據(jù)不需要復(fù)制到用戶(hù)態(tài)空間。當(dāng)應(yīng)用程序往 mmap 輸出數(shù)據(jù)時(shí),此時(shí)就直接輸出到了內(nèi)核態(tài)數(shù)據(jù),如果此時(shí)輸出設(shè)備是磁盤(pán)的話,會(huì)直接寫(xiě)盤(pán)(flush間隔是30秒)。

Kafka是如何利用零拷貝提高性能的


上面的圖片我們可以這樣去理解,比如我們需要從 src.data 文件復(fù)制數(shù)據(jù)到 dest.data 文件中。此時(shí)我們不需要更改 src.data 里面的數(shù)據(jù),但是對(duì)于 dest.data 需要追加一些數(shù)據(jù)。此時(shí)src.data 里面的數(shù)據(jù)可以直接通過(guò)DMA 設(shè)備傳輸,而應(yīng)用程序還需要對(duì) dest.data 做一些數(shù)據(jù)追加,此時(shí)應(yīng)用對(duì) dest.data 做 mmap 映射,直接對(duì)內(nèi)核態(tài)數(shù)據(jù)進(jìn)行修改。

sendfile

對(duì)于sendfile 而言,數(shù)據(jù)不需要在應(yīng)用程序做業(yè)務(wù)處理,僅僅是從一個(gè) DMA 設(shè)備傳輸?shù)搅硪粋€(gè) DMA設(shè)備。 此時(shí)數(shù)據(jù)只需要復(fù)制到內(nèi)核態(tài),用戶(hù)態(tài)不需要復(fù)制數(shù)據(jù),并且也不需要像 mmap 那樣對(duì)內(nèi)核態(tài)的數(shù)據(jù)的句柄(文件引用)。如下圖所示:

Kafka是如何利用零拷貝提高性能的


從上圖我們可以發(fā)現(xiàn)(輸出設(shè)備可以是網(wǎng)卡/磁盤(pán)驅(qū)動(dòng)),內(nèi)核態(tài)有 2 份數(shù)據(jù)緩存 。sendfile 是 Linux 2.1 開(kāi)始引入的,在 Linux 2.4 又做了一些優(yōu)化。也就是上圖中磁盤(pán)頁(yè)緩存中的數(shù)據(jù),不需要復(fù)制到 Socket 緩沖區(qū),而只是將數(shù)據(jù)的位置和長(zhǎng)度信息存儲(chǔ)到 Socket 緩沖區(qū)。實(shí)際數(shù)據(jù)是由DMA 設(shè)備直接發(fā)送給對(duì)應(yīng)的協(xié)議引擎,從而又減少了一次數(shù)據(jù)復(fù)制。

零拷貝的Java實(shí)現(xiàn)

JDK 中的 FileChannel 提供了外部 channel 交互的傳輸方法。transferTo 方法會(huì)將當(dāng)前 FileChannel 的字節(jié)直接傳輸?shù)?channel 中,transferFrom() 方法可以將可讀 channel 的字節(jié)直接傳輸?shù)疆?dāng)前 FileChannel 中。transferTo() 方法底層是基于操作系統(tǒng)的 sendfile 這個(gè)系統(tǒng)調(diào)用來(lái)實(shí)現(xiàn)的,map 是對(duì) Channel 做 mmap 映射。

下面我們看一下 Java NIO 中的方法摘要:


// 將當(dāng)前 FileChannel 的字節(jié)傳輸?shù)浇o定的可寫(xiě) channel 中

public abstract long transferTo(long position, long count, WritableByteChannel target) throws IOException;

// 將一個(gè)可讀 channel 的字節(jié)傳輸?shù)疆?dāng)前 FileChannel中

public abstract long transferFrom(ReadableByteChannel src, long position, long count) throws IOException;

// 對(duì) Channel 做 mmap 映射

public abstract MappedByteBuffer map(MapMode mode, long position, long size) throws IOException;

文件拷貝測(cè)試對(duì)比

下面我們看一下執(zhí)行下面3段代碼,并且 src.log 文件在不同大小的情況下的測(cè)試耗時(shí)結(jié)果。

1、傳統(tǒng)拷貝


public class OldFileCopy {

public static final String source = "C:/data/src.log";

public static final String dest = "C:/data/dest.log";

public static void main(String[] args) {

try {

FileInputStream inputStream = new FileInputStream(source);

FileOutputStream outputStream = new FileOutputStream(dest);

long start = System.currentTimeMillis();

byte[] buff = new byte[4096];

long read = 0, total = 0;

while ((read = inputStream.read(buff)) >= 0) {

total += read;

outputStream.write(buff);

}

outputStream.flush();

System.out.println("耗時(shí):" + (System.currentTimeMillis() - start));

} catch (Exception e) {

e.printStackTrace();

}

}

}

2、mmap 拷貝


public class MmapFileCopy {

public static final String source = "C:/data/src.log";

public static final String dest = "C:/data/dest.log";

public static void main(String[] args) {

try {

FileChannel sourceChannel = new RandomAccessFile(source, "rw").getChannel();

FileChannel destChannel = new RandomAccessFile(dest, "rw").getChannel();

long start = System.currentTimeMillis();

MappedByteBuffer map = destChannel.map(FileChannel.MapMode.READ_WRITE, 0, sourceChannel.size());

sourceChannel.write(map);

map.flip();

System.out.println("耗時(shí):" + (System.currentTimeMillis() - start));

} catch (Exception e) {

e.printStackTrace();

}

}

}

3、sendfile 拷貝

public class SendFileCopy {

public static final String source = "C:/data/src.log";

public static final String dest = "C:/data/dest.log";

public static void main(String[] args) {

try {

FileChannel sourceChannel = new RandomAccessFile(source, "rw").getChannel();

FileChannel destChannel = new RandomAccessFile(dest, "rw").getChannel();

long start = System.currentTimeMillis();

sourceChannel.transferTo(0, sourceChannel.size(), destChannel);

System.out.println("耗時(shí):" + (System.currentTimeMillis() - start));

} catch (Exception e) {

e.printStackTrace();

}

}

}

通過(guò)對(duì)不同大小的文件進(jìn)行對(duì)比測(cè)試,我們得到了下面的測(cè)試結(jié)果。

Kafka是如何利用零拷貝提高性能的

從上面測(cè)試結(jié)果可以看出,mmap 和 sendfile 的方式要遠(yuǎn)遠(yuǎn)優(yōu)于傳統(tǒng)的文件拷貝。對(duì)于 mmap 和 sendfile 在文件較小的時(shí)候, mmap 耗時(shí)更短,當(dāng)文件較大時(shí) sendfile 的方式最優(yōu)。

本文來(lái)自:http://moguhu.com/article/detail?articleId=146

推薦內(nèi)容