直观理解:Kafka零拷贝技术(Zero-Copy)

  在之前的Kafka架构及基本原理简析中已经介绍过了,Kafka的数据是持久化到每个Partition下的.log文件中的,因此当需要消费已经持久化的消息时,势必需要从磁盘中将数据读取到内存中,并通过网卡发送给消费者。之前我们已经说过,Kafka的消息消费是高性能,那么是什么技术支撑了Kafka消息的高性能消费呢?接下来就简单介绍一下Kafka高性能读取的秘密——零拷贝技术。介绍零拷贝技术之前,我们先介绍一下传统的数据文件拷贝过程。

1. 传统数据文件拷贝过程

  传统的数据文件拷贝过程如下图所示,大概可以分成四个过程:
(1)操作系统将数据从磁盘中加载到内核空间的Read Buffer(页缓存区)中。
(2)应用程序将Read Buffer中的数据拷贝到应用空间的应用缓冲区中。
(3)应用程序将应用缓冲区的数据拷贝到内核的Socket Buffer中。
(4)操作系统将数据从Socket Buffer中发送到网卡,通过网卡发送给数据接收方。

传统数据拷贝过程

  通过上图我们发现,传统的数据文件传输需要多次在用户态和核心态之间进行切换,并且需要把数据在用户太和和核心态之间拷贝多次,最终才打到网卡,传输给接收方。那么有没有一种技术,可以是实现数据之间在核心态进行传输,而不需要将数据在核心态和用户态之间来回复制,最终发送给接收端呢?答案是肯定的,下面我们来简单介绍一下Kafka的零拷贝技术。

2. Kafka零拷贝过程

  所谓的零拷贝是指将数据在内核空间直接从磁盘文件复制到网卡中,而不需要经由用户态的应用程序之手。这样既可以提高数据读取的性能,也能减少核心态和用户态之间的上下文切换,提高数据传输效率。
  在正式介绍零拷贝结束(Zero-Copy)之前,我们先简单介绍一下DMA(Direct Memory Access)技术。DMA,又称之为直接内存访问,是零拷贝技术的基石。DMA 传输将数据从一个地址空间复制到另外一个地址空间。当CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。因此通过DMA,硬件则可以绕过CPU,自己去直接访问系统主内存。很多硬件都支持DMA,其中就包括网卡、声卡、磁盘驱动控制器等。
  有了DMA技术的支持之后,网卡就可以直接区访问内核空间的内存,这样就可以实现内核空间和应用空间之间的零拷贝了,极大地提升传输性能。下图展示了Kafka零拷贝的数据传输过程。数据传输的的过程就简化成了:
(1)操作系统将数据从磁盘中加载到内核空间的Read Buffer(页缓存区)中。
(2)操作系统之间将数据从内核空间的Read Buffer(页缓存区)传输到网卡中,并通过网卡将数据发送给接收方。
(3)操作系统将数据的描述符拷贝到Socket Buffer中。Socket 缓存中仅仅会拷贝一个描述符过去,不会拷贝数据到 Socket 缓存。
  Kafka数据零拷贝的过程如下图所示:

零拷贝过程

  通过零拷贝技术,就不需要把 内核空间页缓存里的数据拷贝到应用层缓存,再从应用层缓存拷贝到 Socket 缓存了,两次拷贝都省略了,所以叫做零拷贝。这个过程大大的提升了数据消费时读取文件数据的性能。Kafka 从磁盘读数据的时候,会先看看内核空间的页缓存中是否有,如果有的话,直接通过网关发送出去。

3. Java中零拷贝的实现

  Java中的零拷贝是依靠java.nio.channels.FileChannel中的transferTo(long position, long count, WritableByteChannel target)方法来实现的。transferTo方法的底层实现是基于操作系统的sendfile这个system call来实现的,无需将数据拷贝到用户态,sendfile负责把数据从某个fd(file descriptor)传输到另一个fd。这样就完成了零拷贝的过程。零拷贝的示例代码如下所示:

import java.io.File;
import java.io.RandomAccessFile;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;

public class ZeroCopy {
    public static void main(String[] args) throws Exception {
        File file  = new File("xxxxxx.log");

        RandomAccessFile raf = new RandomAccessFile(file, "rw");

        FileChannel channel = raf.getChannel();

        //Opens a socket channel and connects it to a remote address.
        SocketChannel socketChannel = SocketChannel.open(
                new InetSocketAddress("192.168.2.222", 9091)
        );

        //Transfers bytes from this channel's file to the given writable byte channel.
        channel.transferTo(0,channel.size(), socketChannel);
    }
}

  总结一下:Kafka用到的零拷贝技术,主要是减少了核心态和用户态之间的两次数据拷贝过程,使得数据可以不用经过用户态直接通过网卡发送到接收方,同时通过DMA技术,可以使CPU得到解放,这样实现了数据的高性能传输。

你可能感兴趣的:(直观理解:Kafka零拷贝技术(Zero-Copy))