吴庆龙的技术轮子

学习的目的是输出。

导航

浅谈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又扩展了很多功能。
比如:
  1. 支持 PUSH 模式
  2. 定制化的NameServer(替换了Zookeeper以提供更好的性能)
  3. 有自己的通信协议
  4. 支持消息过滤
  5. 单向消息
  6. 支持全局有序的消息
  7. 支持延时消息
 
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参考资料:

(3)、RocketMQ

也没有遵循 AMQP 协议,也是自己设计的通信协议。