浅谈RabbitMQ 、Kafka及RocketMQ
一、为什么需要消息队列?
(1)、不同系统之间的解耦
(2)、削峰
可以保证下游服务的正常运行,不能因为有大量的请求直接把下游服务给搞挂了。
(3)、异步
将非必要业务写入MQ,加快响应速度。
但也要了解引入消息队列导致的问题:
(1)、增加了系统的复杂度。比如:如何解决消息的重复消费、消息的丢失以及消息的顺序性等问题。
(2)、系统的可用性降低了。外部依赖越多,可用性越差。
二、主流的消息队列有哪些?
(1)、RabbitMQ
官网介绍:RabbitMQ is the most widely deployed open source message broker.
翻译:RabbitMQ是部署最广泛的开源消息代理。
开发语言:Erlang
AMQP 协议的标准实现者,当然也实现了一些其它协议,如 STOMP、MQTT 等。
优点:
(1)、Erlang语言的加持,Erlang是一种面向并发、面向消息的函数式编程语言。
(2)、健壮、稳定、易用、支持多语言、文档齐全。
(3)、管理界面非常棒,是这三个MQ中最好用的。
(4)、社区活跃度挺高的。
缺点:
(1)、不支持重复消费。
(2)、消息堆积过多时,性能表现不佳,应该尽可能的及时消费。
(4)、需要依赖于 HAProxy 做负载均衡。
(5)、Erlang 语言阻止了大量的 Java 工程师去深入研究和掌握它,对公司而言,几乎处于不可控的状态。
(6)、使用了 MPL 开源协议,而 Kafka 和 RocketMQ 都使用了 Apache License。
业务量小的时候可以选择 RabbitMQ,健壮稳定,功能齐全,运维简单,使用简单。
个人觉得 RabbitMQ 官方文档写的非常好:https://www.rabbitmq.com/documentation.html
一位Erlang程序员的自白:https://www.cnblogs.com/xuan52rock/p/4597300.html
(2)、Kafka
官网介绍:Apache Kafka is an open-source distributed event streaming platform used by thousands of companies for high-performance data pipelines, streaming analytics, data integration, and mission-critical applications.
翻译:Apache Kafka是一个开放源代码的分布式事件流平台,成千上万的公司使用它来实现高性能数据管道,流分析,数据集成和关键任务应用程序。
开发语言:Java + Scala
没有遵循 AMQP 协议,是自己基于 TCP 协议定制的协议。
依赖于 Zookeeper主要用于集群管理、配置管理、Leader选举,是Kafka的一部分。
优点:
(1)、消息可以重复消费
(2)、吞吐量高
(3)、社区超级活跃(看起来采用Java开发是一个明智的选择)
缺点:
(1)、没有提供管控台
(2)、不支持死信队列,需要客户端手工实现
(3)、无延时队列
Kafka的客户端代码注释很完整。
(3)、RocketMQ
官网介绍:Apache RocketMQ™ is a unified messaging engine, lightweight data processing platform.
翻译:Apache RocketMQ™是一个统一的消息传递引擎,轻量级的数据处理平台。
开发语言:Java
整体的设计类似于Kafka,但是相对于Kafka又扩展了很多功能。
比如:
- 支持 PUSH 模式
- 定制化的NameServer(替换了Zookeeper以提供更好的性能)
- 有自己的通信协议
- 支持消息过滤
- 单向消息
- 支持全局有序的消息
- 支持延时消息
RocketMQ 设计上与 Kafka 相似,理解了 Kafka 的话对 RocketMQ 的深入就简单多了。
(4)、总结
个人觉得,学习的话,可以先学习 RabbitMQ,再学习 Kafka,再学习 RocketMQ。
MQ 的上手使用都简单,可以学习一下 MQ 的存储机制、读取机制、消费以及生产的规则也是很不错的,很多东西都是互通的。
三、我们知道,生产者产生消息肯定是推送到MQ中的,那么消费者有哪些方式可以拿到消息呢?
有两种方式:
(1)、Push 模型:当 Producer 发出的消息到达后,服务端马上将这条消息投递给 Consumer。
(2)、Pull 模型:当服务端收到这条消息后什么也不做,等着 Consumer 主动到自己这里来读取,即 Consumer 这里有一个“拉取”的动作。
消息队列 | PUSH 模式 | PULL 模式 |
RabbitMQ | 支持 | 支持 |
Kafka | --- | 支持 |
RocketMQ | 支持 | 支持 |
Q:PULL 模式和 PUSH 模式有什么优缺点呢?
Push 模式很难适应消费速率不同的消费者,因为消息发送速率是由 broker 决定的。push 模式的目标是尽可能以最快速度传递消息,但是这样很容易造成 consumer 来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而 pull 模式则可以根据 consumer 的消费能力以适当的速率消费消息。
Kafka 中文文档对于Push or Pull 的解释:https://kafka.apachecn.org/documentation.html#design_pull
四、AMQP(Advanced Message Queuing Protocol)协议
AMQP 是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件同产品,不同的开发语言等条件的限制。实际是基于 TCP 协议。
AMQP模式架构
五、RabbitMQ、kafka及RocketMQ的核心概念
(1)、RabbitMQ
AMQP协议的标准实现者,完全按照 AMQP 模型进行设计的。
主要名词:Broker(Server节点)、虚拟主机(Virtual Host)、交换机(Exchange)、队列(Queue)、路由键(RoutingKey)
数据流转:
虚拟主机默认地址是”/“,在客户端连接的时候指定。主要作用是进行隔离不同的交换机,同一个虚拟主机下不能有相同名称的交换机。
一个队列对应一个 rabbit_queue_index(包含落盘的存储地点、是否被消费者ack等信息),服务端维护一个offset,用来记录这个队列的消息被消费到哪个了。
RabbitMQ消息的存储机制以及队列的结构:https://www.cnblogs.com/Joe-Go/p/10940112.html
注:可以作为学习 MQ 的第一个中间件,理解起来比较容易,数据的流转清晰明了,建立起对 MQ 的整体认知。
(2)、Kafka
没有遵循 AMQP 协议,集群依赖于 Zookeeper,最厉害的地方就是它的高效文件存储机制的设计。
主要名词:Broker(Server节点)、主题(Topic)、分区(Partition)、偏移量(offset),消费者组(Consumer Group)
逻辑上可以把 Topic 理解为一个 Queue。
数据流转:
Topic 下有多个 Partition,每个 Partition 对应一个目录,目录下存储着 Partition 的所有消息和索引文件。
Kafka没有虚拟机的概念.如果要对比RabbitMQ理解的话,也可以理解为一个节点有且只有一个虚拟主机。
Topic 到 Partition 的过程与 RabbitMQ 不同,这里不是通过 RoutingKey 进行路由的,而是根据分区策略将消息存储到不同的 Partition 中,每条消息被顺序追加到 Partition 中。
消息默认的分区策略是:
* 如果在发消息的时候指定了分区,则消息投递到指定的分区
* 如果没有指定分区,但是消息的key不为空,则基于key的哈希值来选择一个分区
* 如果既没有指定分区,且消息的key也是空,则用轮询的方式选择一个分区
Kafka也是用操作系统提供的 mmap 技术来提高数据写入能力(经验证,顺序写磁盘效率比随机写内存还要高,这是Kafka高吞吐率的一个很重要的保证),又通过 sendfile(零拷贝)技术来提高数据的读取能力。
Q:消费者只能指定 Topic 进行消费,不能指定 Topic 中的 Partition 进行消费,那具体哪个消费者消费哪个 Partition 上的数据呢?
A:这里又是Kafka的一个策略,是在客户端实现的(JavaApi 参考 org.apache.kafka.clients.consumer.RangeAssignor 类)。
Q:每个消费者的 offset 值在记录在哪呢?
A:Kafka 服务端不存储每个 Topic 的 Partition 中消息的消费情况(offset 的值),这个值客户端可以控制,并且存储到 Kafka 的 __consumer_offsets Topic 中。
Kafka参考资料:
理解Kafka中的Topic和Partition:https://dongguabai.blog.csdn.net/article/details/86536894
(3)、RocketMQ
也没有遵循 AMQP 协议,也是自己设计的通信协议。
启动步骤:
(4)、消息队列的重要名词
死信队列:消息没有消费者消费、消费者拒绝消费以及消费者不能处理的消息(代码有bug等)会被放入到”死信队列“
延时队列:设置延时时长,比如5秒钟,5秒钟之后消费者才能看到消息
重回队列:消费失败的情况下,将消息重新放入到队列中,类似于重试吧
消息确认:对于生产者来说,就是 Broker 是否收到;对于消费者来说,就是消费者是否收到。
消息返回:消息不知道发给谁时进行监听
(5)、总结
RabbitMQ | Kafka | RocketMQ | |
管控台 | 官方提供,丰富的功能 | 开源三方 | 官方提供(孵化中) |
AMQP 协议 | 完全遵循 | 自定义协议 | 自定义协议 |
消费模式 | PULL 和 PUSH | PULL | PULL 和 PUSH |
开发语言 | Erlang | Java + Scala | Java |
顺序消费 | 队列 | Partition | Topic 或 Partition |
延时队列 | 支持 | 不支持 | 支持 |
死信队列 | 支持 | 不支持 | 不支持 |
刷盘机制 | --- | 异步 | 同步 和 异步 |
重试机制 | 不支持 | 不支持 | 支持定时重试 |
丢失消息 |
四、应用场景
- 如果消息队列不是将要构建系统的重点,对消息队列功能和性能没有很高的要求,只需要一个快速上手易于维护的消息队列,建议使用 RabbitMQ。
- 如果系统使用消息队列主要场景是处理在线业务,比如在交易系统中用消息队列传递订单,需要低延迟和高稳定性,建议使用 RocketMQ。
- 如果需要处理海量的消息,像收集日志、监控信息或是埋点这类数据,或是你的应用场景大量使用了大数据、流计算相关的开源产品,那 Kafka 是最适合的消息队列。
附:
1、开源软件成熟度评测报告-分布式消息中间件 https://mp.weixin.qq.com/s/9L4wz68_BcQrgBw432scYg
2、Apache 孵化项目:https://github.com/apache/rocketmq-externals
------------------------------我是博客签名------------------------------
座右铭:不要因为知识简单就忽略,不积跬步无以至千里。
版权声明:自由转载-非商用-非衍生-保持署名。
本作品采用知识共享署名 4.0 国际许可协议进行许可。
----------------------------------------------------------------------
座右铭:不要因为知识简单就忽略,不积跬步无以至千里。
版权声明:自由转载-非商用-非衍生-保持署名。
本作品采用知识共享署名 4.0 国际许可协议进行许可。
----------------------------------------------------------------------