阿里面试:为什么kafka比RocketMQ吞吐量更高?

阿里中间件团队对 Kafka 和 RocketMQ 进行了性能压测,结果显示在相同条件下,Kafka 的性能比 RocketMQ 快 50%左右。然而,RocketMQ 依然能每秒处理 10 万条消息,这表明它在性能上依然非常强大。那么,为什么 RocketMQ 参考了 Kafka 的架构,却无法与 Kafka 保持相同的性能呢?以下是一些原因:

1.核心差异 mmap(内存映射) vs sendfile

  • Kafka:使用 sendfile 函数进行零拷贝,以减少数据拷贝次数和系统内核切换次数,从而获得更高的性能。sendfile 返回的是发送成功的字节数,而应用层无法获取到消息内容。Kafka 以更少的拷贝次数以及系统内核切换次数,获得了更高的性能。

    但问题又来了,为什么 RocketMQ 不使用 sendfile?参考 Kafka 抄个作业也不难啊?我们来看下 sendfile 函数长啥样:

    ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count);
    // num = sendfile(xxx);
    
  • RocketMQ:使用 mmap 技术进行零拷贝,返回的是数据的具体内容,应用层可以获取消息内容并进行一些逻辑处理。我们来看下 mmap 函数长啥样:

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

    注释里写的是两个函数的用法,mmap 返回的是数据的具体内容,应用层能获取到消息内容并进行一些逻辑处理。而 sendfile 返回的则是发送成功了几个字节数,具体发了什么内容,应用层根本不知道。

    RocketMQ 的一些功能需要了解具体这个消息内容,方便二次投递等,比如将消费失败的消息重新投递到死信队列中。如果 RocketMQ 使用 sendfile,那根本没机会获取到消息内容长什么样子,也就没办法实现一些好用的功能了。而 Kafka 却没有这些功能特性,追求极致性能,正好可以使用 sendfile

操作步骤对比

image.png

传统IO使用 readwrite 的步骤
  1. 从磁盘读取数据

    • 程序发起系统调用 read(),尝试读取磁盘数据。
    • 数据从磁盘设备拷贝到内核空间的缓冲区。
    • 数据再从内核空间的缓冲区拷贝到用户空间的缓冲区。
  2. 将数据发送到网络

    • 程序发起系统调用 write(),将用户空间的缓冲区数据发到网络。
    • 数据从用户空间拷贝到 socket 发送缓冲区。
    • 数据再从 socket 发送缓冲区拷贝到网卡。
  3. 最终数据通过网络到达消费者

使用 mmap 的步骤

mmap 作为一种零拷贝技术,指的是用户空间到内核空间这个过程不需要拷贝,而不是指数据从磁盘到发送到网卡这个过程零拷贝。

  1. 从磁盘读取数据

    • 程序发起系统调用 mmap(),将磁盘文件映射到内存。
    • 数据从磁盘设备拷贝到内核空间的缓冲区。
    • 内核空间的缓冲区映射到用户空间,这里不需要额外的数据拷贝。
  2. 将数据发送到网络

    • 程序发起系统调用 write(),将映射的内存数据发到网络。
    • 数据从内核空间的缓冲区拷贝到 socket 发送缓冲区。
    • 数据再从 socket 发送缓冲区拷贝到网卡。
  3. 最终数据通过网络到达消费者

使用 sendfile 的步骤

这里的零拷贝指的是零 CPU拷贝。也就是说 sendfile 场景下,需要的两次拷贝,都不是 CPU 直接参与的拷贝,而是其他硬件设备技术做的拷贝,不耽误我们 CPU 跑程序。

  1. 从磁盘读取数据并发送

    • 程序发起系统调用 sendfile(),内核会直接从磁盘读取数据并将其发送到网络。
    • 数据从磁盘设备拷贝到内核空间的缓冲区。
    • 内核空间缓冲区里的数据可以直接拷贝到网卡,减少了拷贝次数和系统调用的开销。
  2. 最终数据通过网络到达消费者

2. 结构差异

  • Kafka:使用一个简单的日志存储模型,其中每个主题分区是一个连续的日志。消息被追加到日志文件的末尾,一旦写入就不可改变。这种追加模式极大地优化了磁盘 I/O,因为磁盘写入操作最高效的方式是顺序写入。

  • RocketMQ:在存储层面采用了更为复杂的设计,支持多种存储和索引机制,以便支持广泛的功能,如定时消息和事务消息。这些功能需要在存储系统中实现额外的逻辑,可能影响基本的消息传递性能。

3. 设计目的不同

  • Kafka:其设计哲学是保持简单,尽量减少对消息的处理,直接使用文件系统和网络栈的高效功能。Kafka 追求极致性能,因此其架构设计非常简洁。

  • RocketMQ:提供了更多的灵活性和精细控制,尤其是在消息过滤和服务质量保证上。RocketMQ 的设计考虑了更多的企业级特性和复杂的消息处理需求,这可能在某些场合下牺牲了一部分吞吐量。

4. 数据复制和一致性机制

  • Kafka:使用了一个简化的复制机制,通过 "leader-follower" 模型来同步数据。所有写入操作都先在 leader 上执行,然后异步复制到 follower。这种方法减少了写操作的等待时间,并提高了数据写入的吞吐量。

  • RocketMQ:使用类似的复制机制,但其更复杂的消息路由和存储机制可能增加了额外的开销。例如,RocketMQ 支持更多级别的消息可靠性和复杂的事务管理,这些都可能在提供更高服务质量的同时影响吞吐量。

5. 内存和资源管理

  • Kafka:优化了内存的使用,尽量利用操作系统的页缓存,减少了 JVM 堆内存的压力。这种设计使得 Kafka 能够高效地处理更大量的数据,同时维持低延迟。

  • RocketMQ:对内存和资源的管理也很有效,但它的功能丰富性可能需要更多的资源调整和管理,这在高负载情况下可能影响性能。

6. 消费者模型

  • Kafka:采用拉(pull)模型,允许消费者根据自己的处理能力来拉取数据,这种方式在处理大量数据时可以更好地控制内存和资源使用。

  • RocketMQ:通常也使用拉模型,但它提供了更多的推(push)模式配置选项,这可以更实时地传输消息,但在非常高的吞吐量需求下,推模式可能导致消费者端压力增大。

总结

  • RocketMQ 和 Kafka 相比,在架构上做了减法,在功能上做了加法:RocketMQ 简化了协调节点和分区以及备份模型,同时增强了消息过滤、消息回溯和事务能力,加入了延迟队列、死信队列等新特性。

  • 凡事皆有代价:RocketMQ 牺牲了一部分性能,换取了比 Kafka 更强大的功能特性。

尽管 RocketMQ 参考了 Kafka 的架构,但由于在设计上的不同目标、结构差异以及实现细节上的不同,RocketMQ 在性能上无法完全达到 Kafka 的水平。Kafka 在设计上的简化、对磁盘 I/O 的优化以及高效的数据复制策略,使得它在高吞吐量场景下表现更优。而 RocketMQ 在提供更多企业级功能和服务质量保证的同时,牺牲了一部分性能,但依然表现出色,每秒处理 10 万条消息,证明其强大的处理能力和灵活性。

#程序员##程序员面试#
全部评论
m
点赞 回复 分享
发布于 09-03 12:35 北京
m
点赞 回复 分享
发布于 09-03 13:12 安徽
m
点赞 回复 分享
发布于 09-05 22:13 北京
m
点赞 回复 分享
发布于 09-06 14:17 黑龙江

相关推荐

16 97 评论
分享
牛客网
牛客企业服务