为什么kafka比RocketMQ吞吐量更高?

1 个回答

  • 狄青秋
    世间行乐亦如此,古来万事东流水。
    阿里中间件团队对 Kafka 和 RocketMQ 进行了性能压测,结果显示在相同条件下,Kafka 的性能比 RocketMQ 快 50%左右。然而,RocketMQ 依然能每秒处理 10 万条消息,这表明它在性能上依然非常强大。那么,为什么 RocketMQ 参考了 Kafka 的架构,却无法与 Kafka 保持相同的性能呢?以下是一些原因: ### 1.核心差异 mmap(内存映射) vs sendfile - **Kafka**:使用 `sendfile` 函数进行零拷贝,以减少数据拷贝次数和系统内核切换次数,从而获得更高的性能。`sendfile` 返回的是发送成功的字节数,而应用层无法获取到消息内容。Kafka 以更少的拷贝次数以及系统内核切换次数,获得了更高的性能。 但问题又来了,为什么 RocketMQ 不使用 `sendfile`?参考 Kafka 抄个作业也不难啊?我们来看下 `sendfile` 函数长啥样: ```c ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count); // num = sendfile(xxx); ``` - **RocketMQ**:使用 `mmap` 技术进行零拷贝,返回的是数据的具体内容,应用层可以获取消息内容并进行一些逻辑处理。我们来看下 `mmap` 函数长啥样: ```c 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](https://images.codewd.com/ea20e7ca8456460e329e690c963e8813.jpg "image.png") ##### **传统IO使用 `read` 和 `write` 的步骤** 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在存储结构上采用分区机制(Partition),每个Topic可拆分为多个分区,每个分区对应一个独立的日志文件,写入时按分区路由(如轮询、hash key、业务自定义分区键)决定写入哪个分区。由于各分区独立,Kafka支持多线程并行写入,底层顺序追加,避免磁盘随机IO。Broker内部每个分区由单独线程负责写盘和刷盘,配合操作系统页缓存与零拷贝机制,极大提升写入吞吐。RocketMQ则采用全局单一CommitLog文件,所有Topic和Queue的消息统一串行写入,通过内存队列映射逻辑队列位置,写入需加锁或通过GroupCommit批量控制,写入并发度远低于Kafka。Kafka用分区换并发,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 万条消息,证明其强大的处理能力和灵活性。
    编辑于 2024-04-22 20:06 •IP属地 北京
    举报
    评论
    收藏