RocketMQ 为什么性能不如 Kafka?

RocketMQ 为什么性能不如 Kafka?

声明:本文基于up主小白debug的文章进行了一些延伸用于学习,原文章地址
在上篇文章《rocketmq 是什么》中,我们了解到 RocketMQ 的架构其实参考了 kafka 的设计思想,同时又在 kafka 的基础上做了一些调整。看起来,RocketMQ 好像各方面都比 kafka 更能打。

图片

但 kafka 却一直没被淘汰,说明 RocketMQ 必然是有着不如 kafka 的地方。是啥呢? 性能,严格来说是吞吐量。阿里中间件团队对它们做过压测,同样条件下,kafka 比 RocketMQ 快 50%左右。但即使这样,RocketMQ 依然能每秒处理 10w 量级的数据,依旧非常能打。你不能说 RocketMQ 弱,只能说 Kafka 性能太强了。

不过这就很奇怪了,为什么 RocketMQ 参考了 kafka 的架构,却不能跟 kafka 保持一样的性能呢?在回答这个问题之前,我们来聊下什么是零拷贝

零拷贝是什么

我们知道,消息队列的消息为了防止进程崩溃后丢失,一般不会放内存里,而是放磁盘上。那么问题就来了,消息从消息队列的磁盘,发送到消费者,过程是怎么样的呢?

消息的发送过程

操作系统分为用户空间内核空间。程序处于用户空间,而磁盘属于硬件,操作系统本质上是程序和硬件设备的一个中间层。程序需要通过操作系统去调用硬件能力。

图片

如果用户想要将数据从磁盘发送到网络。那么就会发生下面这几件事:程序会发起系统调用read(),尝试读取磁盘数据,

  • • 磁盘数据从设备拷贝到内核空间的缓冲区。
  • • 再从内核空间的缓冲区拷贝到用户空间。

程序再发起系统调用write(),将读到的数据发到网络:

  • • 数据从用户空间拷贝到 socket 发送缓冲区
  • • 再从 socket 发送缓冲区拷贝到网卡。

最终数据就会经过网络到达消费者。

图片

整个过程,本机内发生了 2系统调用,对应 4 次用户空间和内核空间的切换,以及 4 次数据拷贝

图片

一顿操作猛如虎,结果就是同样一份数据来回拷贝。有没有办法优化呢?有,它就是零拷贝技术,常见的方案有两种,分别是 mmapsendfile。我们来看下它们是什么。

mmap 是什么

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

图片

用了它,整个发送流程就有了一些变化。程序发起系统调用mmap(),尝试读取磁盘数据,具体情况如下:

  • • 磁盘数据从设备拷贝到内核空间的缓冲区。
  • • 内核空间的缓冲区映射到用户空间,这里不需要拷贝。

程序再发起系统调用write(),将读到的数据发到网络:

  • • 数据从内核空间缓冲区拷贝到 socket 发送缓冲区。
  • • 再从 socket 发送缓冲区拷贝到网卡。

图片

整个过程,发生了 2 次系统调用,对应 4 次用户空间和内核空间的切换,以及 3 次数据拷贝,对比之前,省下一次内核空间到用户空间的拷贝。

图片

看到这里大家估计也蒙了,不是说零拷贝吗?怎么还有 3 次拷贝。mmap 作为一种零拷贝技术,指的是用户空间到内核空间这个过程不需要拷贝,而不是指数据从磁盘到发送到网卡这个过程零拷贝。

图片

确实省了一点,但不多。有没有更彻底的零拷贝?有,用 sendfile.

为啥需要mmap呢?

核心矛盾:应用程序需要处理数据
在真实的业务场景中,数据从磁盘读出来之后,应用程序很少会原封不动地直接发出去。它通常需要:

业务逻辑处理:比如,一个Web服务器读取了一个HTML文件,它可能需要解析文件内容,替换其中的模板变量(如用户名、时间等)。

协议封装:比如,给数据加上HTTP头部、加密、压缩等。

数据过滤/转换:比如,只发送数据的某一部分,或者改变数据的格式。

所有这些处理,都必须在用户空间完成,因为应用程序的代码运行在用户空间。

对比两种I/O方案:传统read/write vs mmap零拷贝
方案一:传统的 read()/write()(非零拷贝)

这个过程描述的情况:

  1. read() 系统调用:发生 2次上下文切换

    • 数据流向:磁盘 -> 内核缓冲区 -> 用户缓冲区
    • 这里有一次数据拷贝
  2. 应用程序处理数据:在用户缓冲区中对数据进行修改、加工

  3. write() 系统调用:发生 2次上下文切换

    • 数据流向:用户缓冲区 -> Socket内核缓冲区
    • 这里又有一次数据拷贝
  4. 数据再从 Socket缓冲区 -> 网卡

问题:即使应用程序只是想在数据前面加个Header,也触发了两次完整的数据拷贝(内核到用户,用户到内核)。如果数据量大,这两次CPU拷贝是非常昂贵的开销。


方案二:使用 mmap()(零拷贝之一)

这个过程描述的情况:

  1. mmap() 系统调用:发生上下文切换

    • 并不进行数据拷贝
    • 在用户空间创建一个"虚拟地址映射",直接指向内核缓冲区的那片内存

    比喻:你不是把一份文件复印一份带回家(read拷贝),而是图书馆(内核)给了你一个权限,允许你直接在图书馆的阅览室(内核缓冲区)里阅读和批注那本唯一的书。

  2. 应用程序处理数据:应用程序现在可以直接通过这个映射的地址,在用户空间像操作普通内存一样操作这些数据(它们物理上仍然在内核缓冲区)。当你修改数据时,由于内存映射机制,内核是感知到这些变化的。

  3. write() 系统调用:发生上下文切换

    • 你告诉内核:“把我映射的那片内存数据(也就是内核缓冲区里的数据)发送出去”
    • 内核直接将数据从 内核缓冲区 -> Socket缓冲区

注意:这里仍然有一次内核缓冲区到Socket缓冲区的拷贝。所以mmap并非绝对的"零"拷贝,它避免了内核态和用户态之间的冗余拷贝,但内核内部可能仍有拷贝。

sendfile 是什么

sendfile,也是内核提供的一个方法,从名字可以看出,就是用来发送文件数据的。程序发起系统调用sendfile(),内核会尝试读取磁盘数据然后发送,具体情况如下:

  • • 磁盘数据从设备拷贝到内核空间的缓冲区。
  • • 内核空间缓冲区里的数据可以直接拷贝到网卡。

图片

整个过程,发生了 1 次系统调用,对应 2 次用户空间和内核空间的切换,以及 2 次数据拷贝。这时候问题很多的小明就有意见了,说好的拷贝怎么还有 2 次拷贝?

图片

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

kafka 为什么性能比 RocketMQ 好

聊完两种零拷贝技术,我们回过头来看下 kafka 为什么性能比 RocketMQ 好。这是因为 RocketMQ 使用的是 mmap 零拷贝技术,而 kafka 使用的是 sendfile。kafka 以更少的拷贝次数以及系统内核切换次数,获得了更高的性能。但问题又来了,为什么 RocketMQ 不使用 sendfile?参考 kafka 抄个作业也不难啊?我们来看下 sendfile 函数长啥样。

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

再来看下 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。

除了零拷贝以外,kafka 高性能的原因还有很多,比如什么批处理,数据压缩啥的,但那些优化手段 rocketMQ 也都能借鉴一波,唯独这个零拷贝,那是毫无办法。

图片

所以还是那句话,没有一种架构是完美的,一种架构往往用于适配某些场景,你很难做到既要又要还要。当场景不同,我们就需要做一些定制化改造,通过牺牲一部分能力去换取另一部分能力。做架构,做到最后都是在做折中。是不是感觉升华了。

kafka 和 RocketMQ 怎么选?

这时候大家估计还是想知道 kafka 和 RocketMQ 到底该怎么选,用哪个。官方点的回答是"这个要看场景的"。说了等于没说。这不是我的风格。我的标准只有一个,如果是大数据场景,比如你能频繁听到 spark,flink 这些关键词的时候,那就用 kafka。除此之外,如果公司组件支持,尽量用 RocketMQ。

现在大家通了吗?

总结

  • • RocketMQ 和 kafka 相比,在架构上做了减法,在功能上做了加法
  • • 跟 kafka 的架构相比,RocketMQ 简化了协调节点和分区以及备份模型。同时增强了消息过滤、消息回溯和事务能力,加入了延迟队列,死信队列等新特性。
  • • 凡事皆有代价,RocketMQ 牺牲了一部分性能,换取了比 kafka 更强大的功能特性。
### 回答1: Kafka、RabbitMQ 和 RocketMQ 是三种不同的消息队列系统。Kafka 是一种流式发布/订阅消息系统,它具有高吞吐量和可靠性。RabbitMQ 是一种强大的消息传递系统,可提供灵活的消息路由和消息管理功能。RocketMQ 是一种高性能发布/订阅消息系统,它可以支持百万级别的吞吐量。总之,它们之间的区别在于技术特性、性能、功能,以及它们的应用场景。 ### 回答2: Kafka、RabbitMQ 和 RocketMQ 都是流行的消息中间件,用于实现分布式系统中的消息传递和异步通信。它们有一些相似之处,但也有一些明显的区别。 首先,Kafka 是一个高吞吐量、低延迟的分布式消息队列系统,通常用于处理大规模的实时数据流。Kafka 基于发布/订阅模型,具有持久化、高可靠性、可水平扩展等特点,适用于构建实时流处理和日志处理平台。 RabbitMQ 是一个轻量级的开源消息代理,实现了高度可靠的异步通信。RabbitMQ 使用 AMQP(Advanced Message Queueing Protocol)作为消息传输协议,并支持各种消息模式(如点对点、发布/订阅和工作队列等)。RabbitMQ 非常灵活,易于部署和使用,适合用于构建各种异步通信场景。 RocketMQ 是阿里巴巴开源的分布式消息中间件,专为大规模分布式系统设计。RocketMQ 采用了主题(Topic)和队列(Queue)的概念,支持发布/订阅模式和点对点模式,并提供了丰富的特性,如顺序消息、事务消息、广播消息等。RocketMQ 具有高可靠性、高吞吐量、低延迟和可水平扩展等特点,适用于大规模数据处理和分布式事务场景。 综上所述,Kafka 更适合处理大规模实时数据流,RabbitMQ 则更适合一般的异步通信需求,而 RocketMQ 则专为大规模分布式系统设计,适用于构建复杂的异步通信和分布式事务场景。在选择时,需要根据具体的应用场景和需求进行权衡。 ### 回答3: Kafka、RabbitMQ和RocketMQ都是流行的消息队列系统,虽然它们都用于消息传递,但在某些方面存在一些区别。 1. 发布/订阅模型 vs 点对点模型: - KafkaRocketMQ采用发布/订阅模型,消息通过主题进行分发。消费者可以通过订阅感兴趣的主题来接收消息。 - RabbitMQ使用点对点模型,消息发送者将消息发送到一个特定的队列,消费者从队列中获取消息进行处理。 2. 消息持久化: - KafkaRocketMQ都能存储消息到磁盘,以便在发生故障时进行恢复。它们支持大规模的消息持久化和高吞吐量。 - RabbitMQ默认情况下将消息存储在内存中,除非配置了消息持久化。它主要用于小规模的应用场景。 3. 消息传递的顺序保证: - KafkaRocketMQ都能保证消息以有序的方式进行传递。Kafka通过分区和分区副本来实现有序性。 - RabbitMQ无法保证消息的有序传递,因为消息是根据消费者的连接和处理速度进行分发的。 4. 扩展性: - KafkaRocketMQ都可以通过增加节点来实现水平扩展,提高性能和容错性。 - RabbitMQ使用单台服务器,可通过集群实现高可用性,但扩展性有限。 5. 生态系统和社区支持: - Kafka和RabbitMQ拥有广泛的生态系统和活跃的社区,对于开源和商业方案都有丰富的支持。 - RocketMQ在中国具有广泛的用户群体和大规模的使用情况,但在全球范围内的社区支持相对较弱。 综上所述,Kafka适用于高吞吐与分布式流式处理,RocketMQ适用于大规模分布式的消息传递场景,RabbitMQ适用于简单的应用场景。选择哪个消息队列系统取决于具体的需求、可用资源和技术栈。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值