Kafka与RocketMQ是目前比较常用的两种消息中间件。他们有着高吞吐、低延迟的特性,有强大的消息存储能力和堆积处理能力,可以保证消息的可靠性。下面我们从消费模式、消息存储等多方面,比较一下这两个消息队列。

消息消费模式

消息有两种消费模式,推(Push)和拉(Pull)。

Push:producer发送消息后,broker马上把消息投递给consumer。这种方式好在实时性比较高,但是会增加broker的负载;而且消费端能力不同,如果push推送过快,消费端会出现很多问题。服务端需要保存推送状态、保存订阅关系、消费者复杂均衡;push的实时性好,如果消费能力低,导致消费者崩溃或者大量消费丢失

Pull:producer发送消息后,broker什么也不做,等着consumer自己来读取。它的优点在于主动权在消费者端,可控性好;但是间隔时间不好设置,间隔太短浪费资源,间隔太长又会消费不及时。需要保存消息偏移量offset,异常情况下的各种处理,消息处理不及时,容易引起borker堆积

Kafka:Producer采用发送push的方式将消息发到broker上,broker存储后。由Consumer采用pull模式订阅并消费消息。

RocketMQ:基于Pull模式和Push模式的长轮询机制,来平衡Push和Pull模式各自的优缺点。

长轮询:当Consumer过来请求时,broker会保持当前连接一段时间,如果这段时间内有消息到达,则立刻返回给Consumer;这段时间内没有消息的话则返回空然后重新请求。这种方式的缺点就是服务端要保存consumer状态,客户端过多会一直占用资源。

发送端负载均衡

Kafka:指定partition,直接使用;未指定partition但指定key,通过key的hash选出partition;都未指定,轮询选出partition

RocketMQ:消息指定topic、tag、key,无法指定发送到topic的哪个队列,负载均衡采用轮询

消费者负责均衡

Kafka:以partition为最小单位分配。kafka要求同一个consumerGroup中的两个consumer不能消费同一个分区的消息,但可以消费同一topic不同分区的消息;不同consumerGroup中的consumer可以消费同一分区的消息。

RocketMQ:集群模式consumerGroup均分topic中的消息,广播模式每一条消息对会被consumerGroup中的consumer消费一次。消费端多种负载均衡策略:平均、按机房、按配置、一致性hash等

消息存储

Kafka

每个topic可以分为多个partition, 每个partition是一个有序的队列(独立物理文件)。partition中每一条消息都有一个有序的offset(唯一编号)。partition以文件的形式存储在系统中,包含.log 和 .index 分别为存储具体消息和存储log的索引,log文件名为上一个log文件的最后一个offset值+1。根据offset查找消息,二分查找定位索引文件,二分查找找到对应的offset(小于等于指定索引的最大offset)。打开.log文件,定位到position,顺序往下找。

kafka是一个分区一个文件,当topic过多,分区的总量也会增加,kafka中存在过多的文件,当对消息刷盘时,就会出现文件竞争磁盘,出现性能的下降。一个partition(分区)一个文件,顺序读写。一个分区只能被一个消费组中的一个 消费线程进行消费,因此可以同时消费的消费端也比较少。

RocketMQ

CommitLog ,消息存储文件,所有主题的消息都存储在 CommitLog 文件中。一台 Broker服务器 只有一个 CommitLog 文件(组), RocketMQ 会将所有主题的消息存储在同一个文件中,这个文件中就存储着一条条Message,每条Message都会按照顺序写入。

MessageQueue,数据分片机制,MessageQueue 是和 Broker 绑定在一起的,就是说每个 MessageQueue 都可能处于不同的 Broker 机器上,这取决于你的队列数量和Broker集群。既然 MessageQueue 是多个,那么在消息发送的时候,势必要通过某种方式选择一个队列。默认的情况下,就是通过轮询来获取一个消息队列。

ConsumeQueue,一个topic对应一个ConsumeQueue,ConsumerQueue 也是一组组文件。存储消息下CommitLog中的位置,类似索引,类似Kafka中的partition,但只存索引,消息都在CommitLog中

  • 先通过主题名称,可以定位到具体的文件夹;
  • 然后根据消息队列ID找到具体的文件;
  • 最后根据文件内容,找到具体的消息。

每次读取消息时,先读取ConsumeQueue,在通过ConsumeQueue去CommitLog中拿到消息。写消息大量数据IO顺序写到同一个CommitLog‘中,满1G写新的,累计4K强中从pageCache中刷到磁盘。

消息确认

Kafka:发送方ack,接收方:自动或收到提交offset

RocketMQ:发送方:支持同步、异步确认,同步、异步刷盘,接收方:消费者响应

消息重试

Kafka:offset回溯

RocketMQ:死信队列

高可用

Kafka

无状态集群,一个borker一台机器,分区副本会分布在不同服务器上,每台服务器即使master也是slave。producer会将消息发送个对应partition的leader,leader消息写入本地,followers(追随者)从leader拉去消息同步本地并ack,leader收到ack像producer发送ack

RocketMQ

NameServer无主从关系,只做数据冗余。broker有master和slave,master不可用时切换slave,broker与Nameserver所有集群建立长链接。

总结

Kafka:适合吞吐量高的场景,例如日志处理、大数据处理

kafka性吞吐量更高主要是由于Producer端将多个小消息合并,批量发向Broker。kafka采用异步发送的机制,当发送一条消息时,消息并没有发送到broker而是缓存起来,然后直接向业务返回成功,当缓存的消息达到一定数量时再批量发送。

RocketMQ

适合有序场景,并且抗堆积能力强,峰值高的应用也适合。