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

當前位置: 首頁 > 科技新聞 >

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

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

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

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

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

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


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

DMA

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

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


零拷貝

對于常見的零拷貝,我們下面主要介紹一下 mmapsendfile 兩種方式。下面的介紹我們基于磁盤文件拷貝的方式去講解。

mmap

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

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


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

sendfile

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

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


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

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

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

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


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

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

// 將一個可讀 channel 的字節(jié)傳輸?shù)疆斍?FileChannel中

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

// 對 Channel 做 mmap 映射

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

文件拷貝測試對比

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

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("耗時:" + (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("耗時:" + (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("耗時:" + (System.currentTimeMillis() - start));

} catch (Exception e) {

e.printStackTrace();

}

}

}

通過對不同大小的文件進行對比測試,我們得到了下面的測試結果。

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

從上面測試結果可以看出,mmap 和 sendfile 的方式要遠遠優(yōu)于傳統(tǒng)的文件拷貝。對于 mmap 和 sendfile 在文件較小的時候, mmap 耗時更短,當文件較大時 sendfile 的方式最優(yōu)。

本文來自:http://moguhu.com/article/detail?articleId=146

推薦內容