消息队列Kafka、RabbitMQ、RocketMQ、ActiveMQ 对比

1、入门资料

2、开发语言

Kafka RabbitMQ RocketMQ ActiveMQ
Scala Erlang Java Java

3、支持的协议

Kafka RabbitMQ RocketMQ ActiveMQ
基于TCP自定义 AMQP 自己定义 OpenWire、STOMP、REST、XMPP、AMQP

4、消息存储

Kafka RabbitMQ RocketMQ ActiveMQ
内存、磁盘、数据库。支持大量堆积。 内存、磁盘。支持少量堆积。 磁盘。支持大量堆积。 内存、磁盘、数据库。支持少量堆积。
  • kafka的最小存储单元是分区,一个topic包含多个分区,kafka创建主题时,这些分区会被分配在多个服务器上,通常一个broker一台服务器。分区首领会均匀地分布在不同的服务器上,分区副本也会均匀的分布在不同的服务器上,确保负载均衡和高可用性,当新的broker加入集群的时候,部分副本会被移动到新的broker上。根据配置文件中的目录清单,kafka会把新的分区分配给目录清单里分区数最少的目录。默认情况下,分区器使用轮询算法把消息均衡地分布在同一个主题的不同分区中,对于发送时指定了key的情况,会根据key的hashcode取模后的值存到对应的分区中。
  • Rabbitmq的消息分为持久化的消息和非持久化消息,不管是持久化的消息还是非持久化的消息都可以写入到磁盘。持久化的消息在到达队列时就写入到磁盘,并且如果可以,持久化的消息也会在内存中保存一份备份,这样可以提高一定的性能,当内存吃紧的时候会从内存中清除。非持久化的消息一般只存在于内存中,在内存吃紧的时候会被换入到磁盘中,以节省内存。
    引入镜像队列机制,可将重要队列“复制”到集群中的其他broker上,保证这些队列的消息不会丢失。配置镜像的队列,都包含一个主节点master和多个从节点slave,如果master失效,加入时间最长的slave会被提升为新的master,除发送消息外的所有动作都向master发送,然后由master将命令执行结果广播给各个slave,Rabbitmq会让master均匀地分布在不同的服务器上,而同一个队列的slave也会均匀地分布在不同的服务器上,保证负载均衡和高可用性。
  • RocketMQ的commitLog文件存放实际的消息数据,每个commitLog上限是1G,满了之后会自动新建一个commitLog文件保存数据。ConsumeQueue队列只存放offset、size、tagcode,非常小,分布在多个broker上。ConsumeQueue相当于CommitLog的索引文件,消费者消费时会从consumeQueue中查找消息在commitLog中的offset,再去commitLog中查找元数据。
    ConsumeQueue存储格式的特性,保证了写过程的顺序写盘(写CommitLog文件),大量数据IO都在顺序写同一个commitLog,满1G了再写新的。加上Rocketmq是累计4K才强制从PageCache中刷到磁盘(缓存),所以高并发写性能突出。

5、消息事务

Kafka RabbitMQ RocketMQ ActiveMQ
支持 支持 支持 支持

RabbitMQ客户端将信道设置为事务模式,只有当消息被RabbitMQ接收,事务才能提交成功,否则在捕获异常后进行回滚。使用事务会使得性能有所下降 。

6、负载均衡

Kafka RabbitMQ RocketMQ ActiveMQ
支持 支持的不好 支持 支持
  • Kafka 一个broker通常就是一台服务器节点。对于同一个Topic的不同分区,Kafka会尽力将这些分区分布到不同的Broker服务器上,zookeeper保存了broker、主题和分区的元数据信息。分区首领会处理来自客户端的生产请求,kafka分区首领会被分配到不同的broker服务器上,让不同的broker服务器共同分担任务。
    每一个broker都缓存了元数据信息,客户端可以从任意一个broker获取元数据信息并缓存起来,根据元数据信息知道要往哪里发送请求。kafka的消费者组订阅同一个topic,会尽可能地使得每一个消费者分配到相同数量的分区,分摊负载。当消费者加入或者退出消费者组的时候,还会触发再均衡,为每一个消费者重新分配分区,分摊负载。
    kafka的负载均衡大部分是自动完成的,分区的创建也是kafka完成的,隐藏了很多细节,避免了繁琐的配置和人为疏忽造成的负载问题。发送端由topic和key来决定消息发往哪个分区,如果key为null,那么会使用轮询算法将消息均衡地发送到同一个topic的不同分区中。如果key不为null,那么会根据key的hashcode取模计算出要发往的分区。
  • RabbitMQ消息被投递到哪个队列是由交换器和key决定的,交换器、路由键、队列都需要手动创建。
    RabbitMQ客户端发送消息要和broker建立连接,需要事先知道broker上有哪些交换器,有哪些队列。通常要声明要发送的目标队列,如果没有目标队列,会在broker上创建一个队列,如果有,就什么都不处理,接着往这个队列发送消息。假设大部分繁重任务的队列都创建在同一个broker上,那么这个broker的负载就会过大。(可以在上线前预先创建队列,无需声明要发送的队列,但是发送时不会尝试创建队列,可能出现找不到队列的问题,RabbitMQ的备份交换器会把找不到队列的消息保存到一个专门的队列中,以便以后查询使用),使用镜像队列机制建立RabbitMQ集群可以解决这个问题,形成master-slave的架构,master节点会均匀分布在不同的服务器上,让每一台服务器分摊负载。slave节点只是负责转发,在master失效时会选择加入时间最长的slave成为master。当新节点加入镜像队列的时候,队列中的消息不会同步到新的slave中,除非调用同步命令,但是调用命令后,队列会阻塞,不能在生产环境中调用同步命令。
    当RabbitMQ队列拥有多个消费者的时候,队列收到的消息将以轮询的分发方式发送给消费者。每条消息只会发送给订阅列表里的一个消费者,不会重复。这种方式非常适合扩展,而且是专门为并发程序设计的。如果某些消费者的任务比较繁重,那么可以设置basicQos限制信道上消费者能保持的最大未确认消息的数量,在达到上限时,RabbitMQ不再向这个消费者发送任何消息。
    对于RabbitMQ而言,客户端与集群建立的TCP连接不是与集群中所有的节点建立连接,而是挑选其中一个节点建立连接。但是RabbitMQ集群可以借助HAProxy、LVS技术,或者在客户端使用算法实现负载均衡,引入负载均衡之后,各个客户端的连接可以分摊到集群的各个节点之中。
    客户端均衡算法:
    1)轮询法。按顺序返回下一个服务器的连接地址。
    2)加权轮询法。给配置高、负载低的机器配置更高的权重,让其处理更多的请求;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载。
    3)随机法。随机选取一个服务器的连接地址。
    4)加权随机法。按照概率随机选取连接地址。
    5)源地址哈希法。通过哈希函数计算得到的一个数值,用该数值对服务器列表的大小进行取模运算。
    6)最小连接数法。动态选择当前连接数最少的一台服务器的连接地址。
  • Rocketmq一个broker通常是一个服务器节点,broker分为master和slave,master和slave存储的数据一样,slave从master同步数据。
    nameserver与每个集群成员保持心跳,保存着Topic-Broker路由信息,同一个topic的队列会分布在不同的服务器上。
    发送消息通过轮询队列的方式发送,每个队列接收平均的消息量。发送消息指定topic、tags、keys,无法指定投递到哪个队列(没有意义,集群消费和广播消费跟消息存放在哪个队列没有关系)。tags选填,类似于 Gmail 为每封邮件设置的标签,方便服务器过滤使用。目前只支 持每个消息设置一个 tag,所以也可以类比为 Notify 的 MessageType 概念。keys选填,代表这条消息的业务关键词,服务器会根据 keys 创建哈希索引,设置后, 可以在 Console 系统根据 Topic、Keys 来查询消息,由于是哈希索引,请尽可能保证key唯一,例如订单号,商品 Id 等。
    Rocketmq的负载均衡策略规定:Consumer数量应该小于等于Queue数量,如果Consumer超过Queue数量,那么多余的Consumer 将不能消费消息。这一点和kafka是一致的,rocketmq会尽可能地为每一个Consumer分配相同数量的队列,分摊负载。

7、集群方式

Kafka RabbitMQ RocketMQ ActiveMQ
天然的‘Leader-Slave’无状态集群,每台服务器既是Master也是Slave。 支持简单集群,'复制’模式,对高级集群模式支持不好。 常用 多对’Master-Slave’ 模式,开源版本需手动切换Slave变成Master 支持简单集群模式,比如’主-备’,对高级集群模式支持不好。

8、可用性

Kafka RabbitMQ RocketMQ ActiveMQ
非常高(分布式) 高(主从) 非常高(分布式) 高(主从)

9、消息重复

Kafka RabbitMQ RocketMQ ActiveMQ
支持at least once、at most once 支持at least once、at most once 支持at least once 支持at least once

10、吞吐量TPS

Kafka RabbitMQ RocketMQ ActiveMQ
极大 Kafka按批次发送消息和消费消息。 比较大 大 rocketMQ接收端可以批量消费消息,可以配置每次消费的消息数,但不是批量发送。 比较大

11、顺序消息

Kafka RabbitMQ RocketMQ ActiveMQ
支持 不支持 支持 不支持

12、消息确认

Kafka RabbitMQ RocketMQ ActiveMQ
支持 支持 支持 支持
  • Kafka发送方确认机制:ack=0,不管消息是否成功写入分区;ack=1,消息成功写入首领分区后,返回成功;ack=all,消息成功写入所有分区后,返回成功。
    接收方确认机制:自动或者手动提交分区偏移量,早期版本的kafka偏移量是提交给Zookeeper的,这样使得zookeeper的压力比较大,更新版本的kafka的偏移量是提交给kafka服务器的,不再依赖于zookeeper群组,集群的性能更加稳定。
  • Rabbitmq发送方确认机制,消息被投递到所有匹配的队列后,返回成功。如果消息和队列是可持久化的,那么在写入磁盘后,返回成功。支持批量确认和异步确认。
    接收方确认机制,设置autoAck为false,需要显式确认,设置autoAck为true,自动确认。
    当autoAck为false的时候,Rabbitmq队列会分成两部分,一部分是等待投递给consumer的消息,一部分是已经投递但是没收到确认的消息。如果一直没有收到确认信号,并且consumer已经断开连接,Rabbitmq会安排这个消息重新进入队列,投递给原来的消费者或者下一个消费者。未确认的消息不会有过期时间,如果一直没有确认,并且没有断开连接,Rabbitmq会一直等待,Rabbitmq允许一条消息处理的时间可以很久很久。

13、消息重试

Kafka RabbitMQ RocketMQ ActiveMQ
不支持,但是支持指定分区offset位置的回溯,可以实现消息重试 不支持,但是可以利用消息确认机制实现。 支持 不支持

消息消费失败的大部分场景下,立即重试99%都会失败,所以Rocketmq的策略是在消费失败时定时重试,每次时间间隔相同。
1)发送端的 send 方法本身支持内部重试,重试逻辑如下:
a)至多重试3次;
b)如果发送失败,则轮转到下一个broker;
c)这个方法的总耗时不超过sendMsgTimeout 设置的值,默认 10s,超过时间不在重试。
2)接收端:
Consumer 消费消息失败后,要提供一种重试机制,令消息再消费一次。Consumer 消费消息失败通常可以分为以下两种情况:
a)由于消息本身的原因,例如反序列化失败,消息数据本身无法处理等。定时重试机制,比如过 10s 秒后再重试。
b)由于依赖的下游应用服务不可用,例如 db 连接不可用,外系统网络不可达等。
即使跳过当前失败的消息,消费其他消息同样也会报错。这种情况可以 sleep 30s,再消费下一条消息,减轻 Broker 重试消息的压力。

参考文章:https://mp.weixin.qq.com/s/J6eBMis0KpJ6Ae91HSsRuA

发布了105 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43792385/article/details/102929938