阿里面试:为什么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
。
操作步骤对比
传统IO使用 read
和 write
的步骤
-
从磁盘读取数据
- 程序发起系统调用
read()
,尝试读取磁盘数据。 - 数据从磁盘设备拷贝到内核空间的缓冲区。
- 数据再从内核空间的缓冲区拷贝到用户空间的缓冲区。
- 程序发起系统调用
-
将数据发送到网络
- 程序发起系统调用
write()
,将用户空间的缓冲区数据发到网络。 - 数据从用户空间拷贝到 socket 发送缓冲区。
- 数据再从 socket 发送缓冲区拷贝到网卡。
- 程序发起系统调用
-
最终数据通过网络到达消费者
使用 mmap
的步骤
mmap 作为一种零拷贝技术,指的是用户空间到内核空间这个过程不需要拷贝,而不是指数据从磁盘到发送到网卡这个过程零拷贝。
-
从磁盘读取数据
- 程序发起系统调用
mmap()
,将磁盘文件映射到内存。 - 数据从磁盘设备拷贝到内核空间的缓冲区。
- 内核空间的缓冲区映射到用户空间,这里不需要额外的数据拷贝。
- 程序发起系统调用
-
将数据发送到网络
- 程序发起系统调用
write()
,将映射的内存数据发到网络。 - 数据从内核空间的缓冲区拷贝到 socket 发送缓冲区。
- 数据再从 socket 发送缓冲区拷贝到网卡。
- 程序发起系统调用
-
最终数据通过网络到达消费者
使用 sendfile
的步骤
这里的零拷贝指的是零 CPU拷贝。也就是说 sendfile 场景下,需要的两次拷贝,都不是 CPU 直接参与的拷贝,而是其他硬件设备技术做的拷贝,不耽误我们 CPU 跑程序。
-
从磁盘读取数据并发送
- 程序发起系统调用
sendfile()
,内核会直接从磁盘读取数据并将其发送到网络。 - 数据从磁盘设备拷贝到内核空间的缓冲区。
- 内核空间缓冲区里的数据可以直接拷贝到网卡,减少了拷贝次数和系统调用的开销。
- 程序发起系统调用
-
最终数据通过网络到达消费者
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 万条消息,证明其强大的处理能力和灵活性。
#程序员##程序员面试#