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

栏目: IT技术 · 发布时间: 4年前

内容简介:Kafka 在执行消息的写入和读取这么快的原因,其中的一个原因是零拷贝(Zero-copy)技术,下面我们来了解一下这么高效的原因。传统的文件读写传统的文件读写或者网络传输,通常需要将数据从

Kafka 在执行消息的写入和读取这么快的原因,其中的一个原因是零拷贝(Zero-copy)技术,下面我们来了解一下这么高效的原因。

传统的文件读写

传统的文件读写或者网络传输,通常需要将数据从 内核态 转换为 用户态 。应用程序读取用户态内存数据,写入文件 / Socket之前,需要从用户态转换为内核态之后才可以写入文件或者网卡当中。

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

数据首先从磁盘读取到内核缓冲区,这里面的内核缓冲区就是页缓存(PageCache)。然后从内核缓冲区中复制到应用程序缓冲区(用户态),输出到输出设备时,又会将用户态数据转换为内核态数据。

DMA

在介绍零拷贝之前,我们先来看一个技术名词DMA(Direct Memory Access 直接内存访问)。它是现代电脑的重要特征之一,允许不同速度的硬件之间直接交互,而不需要占用CPU的中断负载。DMA传输将一个地址空间复制到另一个地址空间,当CPU 初始化这个传输之后,实际的数据传输是有DMA设备之间完成,这样可以大大的减少CPU的消耗。我们常见的硬件设备都支持DMA,如下图所示:

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

零拷贝

对于常见的零拷贝,我们下面主要介绍一下 mmapsendfile 两种方式。下面的介绍我们基于磁盘文件拷贝的方式去讲解。

mmap

mmap 就是在用户态直接引用文件句柄,也就是用户态和内核态共享内核态的数据缓冲区,此时数据不需要复制到用户态空间。当应用程序往 mmap 输出数据时,此时就直接输出到了内核态数据,如果此时输出设备是磁盘的话,会直接写盘(flush间隔是30秒)。

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

上面的图片我们可以这样去理解,比如我们需要从 src.data 文件复制数据到 dest.data 文件中。此时我们不需要更改 src.data 里面的数据,但是对于 dest.data 需要追加一些数据。此时src.data 里面的数据可以直接通过DMA 设备传输,而应用程序还需要对 dest.data 做一些数据追加,此时应用对 dest.data 做 mmap 映射,直接对内核态数据进行修改。

sendfile

对于sendfile 而言,数据不需要在应用程序做业务处理,仅仅是从一个 DMA 设备传输到另一个 DMA设备。 此时数据只需要复制到内核态,用户态不需要复制数据,并且也不需要像 mmap 那样对内核态的数据的句柄(文件引用)。如下图所示:

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

从上图我们可以发现(输出设备可以是网卡/磁盘驱动),内核态有 2 份数据缓存 。sendfile 是 Linux 2.1 开始引入的,在 Linux 2.4 又做了一些优化。也就是上图中磁盘页缓存中的数据,不需要复制到 Socket 缓冲区,而只是将数据的位置和长度信息存储到 Socket 缓冲区。实际数据是由DMA 设备直接发送给对应的协议引擎,从而又减少了一次数据复制。

零拷贝的 Java 实现

JDK 中的 FileChannel 提供了外部 channel 交互的传输方法。transferTo 方法会将当前 FileChannel 的字节直接传输到 channel 中,transferFrom() 方法可以将可读 channel 的字节直接传输到当前 FileChannel 中。transferTo() 方法底层是基于操作系统的 sendfile 这个系统调用来实现的,map 是对 Channel 做 mmap 映射。

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

// 将当前 FileChannel 的字节传输到给定的可写 channel 中

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

// 将一个可读 channel 的字节传输到当前 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;

文件拷贝测试对比

下面我们看一下执行下面3段代码,并且 src.log 文件在不同大小的情况下的测试耗时结果。

1、传统拷贝

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 的方式要远远优于传统的文件拷贝。对于 mmap 和 sendfile 在文件较小的时候, mmap 耗时更短,当文件较大时 sendfile 的方式最优。

本文来自:

http://moguhu.com/article/detail?articleId=146


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Flask Web开发实战

Flask Web开发实战

李辉 / 机械工业出版社 / 2018-8-1 / 129

这是一本面向Python程序员的,全面介绍Python Web框架Flask的书。关于本书的详细介绍、相关资源等更多信息可以访问本书的官方主页http://helloflask.com/book了解。 • 国内首本Flask著作,在内容上涵盖完整的Flask Web开发学习路径,在实践上包含完整的Flask Web程序开发流程。同时兼容Python2 .7和Python3.6。 • 内......一起来看看 《Flask Web开发实战》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

随机密码生成器
随机密码生成器

多种字符组合密码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具