Kafka凭啥这么快

kakfa是什么?Kafka的架构原理和应用场景是什么?

RocketMQ是什么?为什么RocketMQ性能不如Kafka?

RocketMQ 和 Kafka 怎么选?

零拷贝是什么?

mmap是什么?

sendfile是什么?

RocketMQ为什么不使用sendfile?

1. Kafka 为何没有被 RocketMQ 替代?

RocketMQ 相比于 Kafka, 简化了架构, 丰富了功能, 却依旧没能取代 Kafka, 那么 RocketMQ 一定有不如 Kafka 的地方

性能 & 吞吐量

  • RocketMQ: 1w/s Proc
  • Kafka: 17w/s Proc

2. 零拷贝是什么?

我们知道消息队列的消息为了防止进程崩溃后,一般不会放内存里,而是放磁盘上。

消息发送过程

操作系统分为内核空间和用户空间,程序处于用户空间,而磁盘属于硬件;

操作系统本质上是程序和硬件设备的一个中间层程序,程序需要通过操作系统去调用硬件能力。

+-------------+
|  Program    |                +---------+        
+-------------+                | Program |        
                             +-+---------+-+      
+-------------+       ------>|  User Space |      
|             |------/       +-------------+      
|     OS      |                                   
|             |------\       +---------------+    
+-------------+       ------>| Kernel Space  |    
                             +---------------+    
+-------------+                                                                  
|  Hardware   |                                   
+-------------+                                   

如果用户想要将数据从磁盘发送到网络,那么就会发生下面这几件事

        +-----------------------------+
        |                             |
  User  | read()              write() |
  Space |        user buffer          |
        |          ^     \            |
        +---------/-------\-----------+
 socket |        /         \          |
        +-------/-----------\---------+
        |      /             \        |
 Kernal |     /               v       |
 Space  |kernal buffer   socket buffer|
        |   ^                   \     |
        +--/---------------------\----+
          /                       v    
        disk                     NIC   
  1. 程序发起系统调用 read() 将磁盘数据从设备拷贝到内核空间的缓冲区
  2. 从内核空间的缓冲区拷贝到用户空间
  3. 程序在发起系统调用 write() 将数据从用户空间拷贝到 socket 的发送缓冲区
  4. 再从 socket 的发送缓冲区拷贝到网卡
  5. 最终数据会会经过网络到达消费者

整个过程本机内发生了两次系统调用: read()/write()

对应四次用户空间到内核空间切换: read 调用/返回, write 调用/返回

以及四次数据拷贝

优化

零拷贝技术常见的方案有两种,分别是 mmap 和 sendfile

mmap

mmap 是操作系统内核提供的一个方法,以将内核空间的缓冲区映射到用户空间

        +-----------------------------+   
        |                             |   
  User  |mmap()               write() |   
  Space |        user buffer          |   
        |          ^                  |   
        +---------/-------------------+   
 socket |      map                    |   
        +-------/---------------------+   
        |      /                      |   
 Kernal |     v                socket |   
 Space  |kernal buffer <------>buffer |   
        |   ^                     |   |   
        +--/----------------------|---+   
          /                       v       
        disk                     NIC      
  1. 程序发起系统调用 mmap() 将磁盘数据从设备拷贝到内核空间的缓冲区,
  2. 内核空间的缓冲区,映射到用户空间
  3. 这里不需要拷贝程序,再发起系统调用 write(), 数据从内核空间缓冲区拷贝到 socket 发送缓冲区
  4. 从 socket 发送缓冲区拷贝到网卡

整个过程本机内发生了两次系统调用: mmap()/write()

对应四次用户空间到内核空间切换: mmap 调用/返回, write 调用/返回

以及 三次数据拷贝

对比之前, 省下一次内核空间到用户空间的拷贝

mmap 作为一种零拷贝技术,指的是用户空间到内核空间,这个过程不需要拷贝

sendfile

sendfile 也是内核提供的一个方法, 就是用来发送文件数据的

        +-----------------------------+         
        |                             |         
  User  |                             |         
  Space |                 sendfile()  |         
        |                             |         
        +-----------------------------+         
 socket |                             |         
        +-----------------------------+         
        |                             |         
 Kernal |       kernal buffer         |         
 Space  |        ^          \         |         
        |       /          SG-DMA     |         
        +------/--------------\-------+         
              /                v                
           disk                NIC              
  1. 程序发起系统调用 sendfile 内核会将数据从磁盘设备拷贝到内核空间的缓冲区
  2. 内核空间缓冲区里的数据可以直接拷贝到网卡

整个过程发生了一次系统调用,对应两次用户空间和内核空间的切换: sendfile 调用/返回

以及 两次数据拷贝

sendfile 作为一种零拷贝技术,指的是零 CPU 拷贝

sendfile 场景下需要的两次拷贝都不是 CPU 直接参与的拷贝,而是 DMA 控制器在干活

Kafka 为什么性能比 RocketMQ 好?

这是因为 RocketMQ 使用的是 mmap 技术, 而 kafka 使用的是 sendfile; kafka 以更少的拷贝次数以及系统内核切换次数获得了更高的性能。

为什么 RocketMQ 不使用 sendfile?

sendfile 函数长啥样

int sendfile(int out_fd, in_fd, off_t* offset, int count);
// num = sendfile(xxx);

sendfile 返回的则是成功发送了几个字结束,具体发了什么内容,应用层根本不知道。

mmap 函数长啥样

void *mmap(void *addr, int length, prot, flags, fd, off_t offset);
// buf = mmap(xxx);

mmap 返回的是数据的具体内容,应用层能获取到消息内容,并进行一些逻辑处理。

总结

而 RocketMQ 的一些功能,却需要了解具体的消息内容,方便二次投递的,比如将消费失败的消息重新投递到死性队列中。

如果 RocketMQ 使用 sendfile,那根本没机会获取到消息内容长什么样子,也就没办法实现一些好用的功能了,而 kafka 却没有这些功能特性,除求极致性能正好可以使用。