之前公司因为业务解耦和系统重构,打算引入消息队列,经过一系列的选型,最后选择使用阿里系的RocketMQ。
最初我们选择直接使用阿里云平台上提供的MQ服务,但是由于后面的业务增长,平台服务的收费比较昂贵,所以打算使用自建的MQ服务,当然在使用之前我对自建服务进行了性能压测,有关RocketMQ性能压测会在后面的文章进行分享,测试结果发现MQ的性能还是杠杠的,理论上是完全能支撑现有的业务。
这一篇分享是我在部门技术分享时候对使用RocketMQ后的一些总结,今天重新梳理了再次分享给更多的同学。
使用场景
🌟 异步处理
🌟 应用解耦
🌟 流量削峰
🌟日志处理
消息队列
- Kafka
- RabbitMQ
- ZeroMQ
- RocketMQ
- ActiveMQ
- Pulsar
1. 资料文档💤
- Kafka
- 有kafka作者自己写的书,网上资料也有一些
- RabbitMQ
- 多。有一些不错的书,网上资料多
- ZeroMQ
- 少。没有专门写zeromq的书,网上的资料多是一些代码的实现和简单介绍
- RocketMQ
- 少。没有专门写rocketmq的书,网上的资料良莠不齐,官方文档很简洁,但是对技术细节没有过多的描述
- ActiveMQ
- 多。没有专门写activemq的书,网上资料多
2.开发语言💤
- Kafka
- Scala
- RabbitMQ
- Erlang
- ZeroMQ
- c
- RocketMQ
- java
- ActiveMQ
- java
3. 支持协议💤
- Kafka
- 自定义TCP协议
- RabbitMQ
- AMQP
- ZeroMQ
- TCP、UDP
- RocketMQ
- 自定义TCP协议
- ActiveMQ
- OpenWire、STOMP、REST、XMPP、AMQP
4. 消息存储💤
-
Kafka
- 内存、磁盘、数据库。支持大量堆积。
kafka的最小存储单元是分区,一个topic包含多个分区,kafka创建主题时,这些分区会被分配在多个服务器上,通常一个broker一台服务器。 分区首领会均匀地分布在不同的服务器上,分区副本也会均匀的分布在不同的服务器上,确保负载均衡和高可用性,当新的broker加入集群的时候,部分副本会被移动到新的broker上。 根据配置文件中的目录清单,kafka会把新的分区分配给目录清单里分区数最少的目录。 默认情况下,分区器使用轮询算法把消息均衡地分布在同一个主题的不同分区中,对于发送时指定了key的情况,会根据key的hashcode取模后的值存到对应的分区中。
- 内存、磁盘、数据库。支持大量堆积。
-
RabbitMQ
- 内存、磁盘。支持少量堆积。
rabbitmq的消息分为持久化的消息和非持久化消息,不管是持久化的消息还是非持久化的消息都可以写入到磁盘。 持久化的消息在到达队列时就写入到磁盘,并且如果可以,持久化的消息也会在内存中保存一份备份,这样可以提高一定的性能,当内存吃紧的时候会从内存中清除。非持久化的消息一般只存在于内存中,在内存吃紧的时候会被换入到磁盘中,以节省内存。引入镜像队列机制,可将重要队列“复制”到集群中的其他broker上,保证这些队列的消息不会丢失。配置镜像的队列,都包含一个主节点master和多个从节点slave,如果master失效,加入时间最长的slave会被提升为新的master,除发送消息外的所有动作都向master发送,然后由master将命令执行结果广播给各个slave,rabbitmq会让master均匀地分布在不同的服务器上,而同一个队列的slave也会均匀地分布在不同的服务器上,保证负载均衡和高可用性。
- 内存、磁盘。支持少量堆积。
-
ZeroMQ
- 消息发送端的内存或者磁盘中。不支持持久化
-
RocketMQ
- 磁盘。支持大量堆积
commitLog文件存放实际的消息数据,每个commitLog上限是1G,满了之后会自动新建一个commitLog文件保存数据。ConsumeQueue队列只存放offset、size、tagcode,非常小,分布在多个broker上。ConsumeQueue相当于CommitLog的索引文件,消费者消费时会从consumeQueue中查找消息在commitLog中的offset,再去commitLog中查找元数据。
ConsumeQueue存储格式的特性,保证了写过程的顺序写盘(写CommitLog文件),大量数据IO都在顺序写同一个commitLog,满1G了再写新的。加上rocketmq是累计4K才强制从PageCache中刷到磁盘(缓存),所以高并发写性能突出。
- 磁盘。支持大量堆积
-
ActiveMQ
- 内存、磁盘、数据库。支持少量堆积。
5.消息事物💤
- Kafka
- 支持
- RabbitMQ
- 支持
- ZeroMQ
- 不支持
- RocketMQ
- 支持
- ActiveMQ
- 支持
6.负载均衡
-
Kafka
- 一个broker通常就是一台服务器节点。对于同一个Topic的不同分区,Kafka会尽力将这些分区分布到不同的Broker服务器上,zookeeper保存了broker、主题和分区的元数据信息。分区首领会处理来自客户端的生产请求,kafka分区首领会被分配到不同的broker服务器上,让不同的broker服务器共同分担任务。
- 发送端由topic和key来决定消息发往哪个分区,如果key为null,那么会使用轮询算法将消息均衡地发送到同一个topic的不同分区中。如果key不为null,那么会根据key的hashcode取模计算出要发往的分区
-
RabbitMQ
- 对负载均衡的支持不好。消息被投递到哪个队列是由交换器和key决定的,交换器、路由键、队列都需要手动创建
- 但是rabbitmq集群可以借助HAProxy、LVS技术,或者在客户端使用算法实现负载均衡,引入负载均衡之后,各个客户端的连接可以分摊到集群的各个节点之中
-
ZeroMQ
- 去中心化,不支持负载均衡。本身只是一个多线程网络库
-
RocketMQ
- 支持负载均衡。发送消息通过轮询队列的方式发送,每个队列接收平均的消息量。发送消息指定topic、tags、keys,无法指定投递到哪个队列(没有意义,集群消费和广播消费跟消息存放在哪个队列没有关系)
- rocketmq的负载均衡策略规定:Consumer数量应该小于等于Queue数量,如果Consumer超过Queue数量,那么多余的Consumer 将不能消费消息。这一点和kafka是一致的,rocketmq会尽可能地为每一个Consumer分配相同数量的队列,分摊负载
-
ActiveMQ
- 支持负载均衡。可以基于zookeeper实现负载均衡。
7.集群方式💤
-
Kafka
- 天然的‘Leader-Slave’无状态集群,每台服务器既是Master也是Slave。
分区首领均匀地分布在不同的kafka服务器上,分区副本也均匀地分布在不同的kafka服务器上,所以每一台kafka服务器既含有分区首领,同时又含有分区副本,每一台kafka服务器是某一台kafka服务器的Slave,同时也是某一台kafka服务器的leader。
kafka的集群依赖于zookeeper,zookeeper支持热扩展,所有的broker、消费者、分区都可以动态加入移除,而无需关闭服务,与不依靠zookeeper集群的mq相比,这是最大的优势
- 天然的‘Leader-Slave’无状态集群,每台服务器既是Master也是Slave。
-
RabbitMQ
- 支持简单集群,'复制'模式,对高级集群模式支持不好
rabbitmq的每一个节点,不管是单一节点系统或者是集群中的一部分,要么是内存节点,要么是磁盘节点,集群中至少要有一个是磁盘节点。
在rabbitmq集群中创建队列,集群只会在单个节点创建队列进程和完整的队列信息(元数据、状态、内容),而不是在所有节点上创建。
引入镜像队列,可以避免单点故障,确保服务的可用性,但是需要人为地为某些重要的队列配置镜像。
- 支持简单集群,'复制'模式,对高级集群模式支持不好
-
ZeroMQ
- 去中心化,不支持集群
-
RocketMQ
- 常用多对'Master-Slave'模式,开源版本需手动切换Slave变成Master
一个topic有多个队列,这些队列会均匀地分布在不同的broker服务器上。rocketmq队列的概念和kafka的分区概念是基本一致的,kafka同一个topic的分区尽可能地分布在不同的broker上,分区副本也会分布在不同的broker上。
rocketmq集群的slave会从master拉取数据备份,master分布在不同的broker上
- 常用多对'Master-Slave'模式,开源版本需手动切换Slave变成Master
-
ActiveMQ
- 支持简单集群模式,比如'主-备',对高级集群模式支持不好
8.可用性💤
- Kafka
- 非常高(分布式)
- RabbitMQ
- 高(主从)
- ZeroMQ
- 高
- RocketMQ
- 非常高(分布式)
- ActiveMQ
- 高(主从)
9.消息重复💤
- Kafka
- 支持at least once、at most once
- RabbitMQ
- 支持at least once、at most once
- ZeroMQ
- 只有重传机制,但是没有持久化,消息丢了重传也没有用。既不是at least once、也不是at most once、更不是exactly only once
- RocketMQ
- 支持at least once
- ActiveMQ
- 支持at least once
10.吞吐量TPS💤
- Kafka
- 极大。Kafka按批次发送消息和消费消息。发送端将多个小消息合并,批量发向Broker,消费端每次取出一个批次的消息批量处理。
- RabbitMQ
- 比较大
- ZeroMQ
- 极大
- RocketMQ
- 大。rocketMQ接收端可以批量消费消息,可以配置每次消费的消息数,但是发送端不是批量发送。
- ActiveMQ
- 比较大
11.顺序消费💤
- Kafka
- 支持
- RabbitMQ
- 不支持
- ZeroMQ
- 不支持
- RocketMQ
- 支持
- ActiveMQ
- 不支持
12.消息回溯💤
- Kafka
- 支持。指定分区offset位置的回溯
- RabbitMQ
- 不支持
- ZeroMQ
- 不支持
- RocketMQ
- 支持。指定分区offset位置的回溯
- ActiveMQ
- 不支持
13.并发度💤
-
Kafka
- 高。
一个线程一个消费者,kafka限制消费者的个数要小于等于分区数,如果要提高并行度,可以在消费者中再开启多线程,或者增加consumer实例数量
- 高。
-
RabbitMQ
- 极高。
本身是用Erlang语言写的,并发性能高。可在消费者中开启多线程,最常用的做法是一个channel对应一个消费者,每一个线程把持一个channel,多个线程复用connection的tcp连接,减少性能开销
- 极高。
-
ZeroMQ
- 高
-
RocketMQ
- 高
1>rocketmq限制消费者的个数少于等于队列数,但是可以在消费者中再开启多线程,这一点和kafka是一致的,提高并行度的方法相同。
修改消费并行度方法
a) 同一个 ConsumerGroup 下,通过增加 Consumer 实例数量来提高并行度,超过订阅队列数的 Consumer实例无效。
b) 提高单个 Consumer 的消费并行线程,通过修改参数consumeThreadMin、consumeThreadMax
2>同一个网络连接connection,客户端多个线程可以同时发送请求,连接会被复用,减少性能开销。
- 高
-
ActiveMQ
- 高
😅😅😅 那我们为什么要选择Rocketmq呢❓❓❓
1️⃣ 强调集群无单点,可扩展,任意一点高可用,水平可扩展
2️⃣ 海量消息堆积能力,消息堆积后,写入低延迟
3️⃣ 支持上万个队列(与ActiveMQ进行对比)
4️⃣ 消息失败重试机制
5️⃣ 消息可查询
6️⃣ 开源社区活跃
7️⃣ 成熟度(经过双十一考验)