消息队列工作原理对比以及选型

26,217 阅读10分钟

一:抛出问题

消息队列中间件重要吗?⾯试必问问题之⼀,你说重不重要。我有时会问同事,为啥你⽤ RabbitMQ,不⽤ Kafka,或者 RocketMQ 呢,他给我的回答 “因为公司⽤的就是这个,⼤家都这么⽤”,如果你去⾯试,直接就被 Pass,今天这篇⽂章,告诉你如何回答。

这篇⽂章,我重点突出消息队列选型,弱化每种队列内部的实现细节,精华提炼,可读性更强!

常⽤的消息队列主要这 4 种,分别为 Kafka、RabbitMQ、RocketMQ 和 ActiveMQ,主要介绍前三,不BB,上思 维导图!

image.png

二:消息队列基础

2.1 什么是消息队列?

消息队列是在消息的传输过程中保存消息的容器,⽤于接收消息并以⽂件的⽅式存储,⼀个消息队列可以被⼀个也 可以被多个消费者消费,包含以下 3 元素:

  • Producer:消息⽣产者,负责产⽣和发送消息到 Broker;
  • Broker:消息处理中⼼,负责消息存储、确认、重试等,⼀般其中会包含多个 Queue;
  • Consumer:消息消费者,负责从 Broker 中获取消息,并进⾏相应处理。

image.png

2.2 消息队列模式

  • 点对点模式:多个⽣产者可以向同⼀个消息队列发送消息,⼀个具体的消息只能由⼀个消费者消费。

image.png

  • 发布/订阅模式:单个消息可以被多个订阅者并发的获取和处理。

image.png

2.3 消息队列应⽤场景

  • 应⽤解耦:消息队列减少了服务之间的耦合性,不同的服务可以通过消息队列进⾏通信,⽽不⽤关⼼彼此的实 现细节。
  • 异步处理:消息队列本身是异步的,它允许接收者在消息发送很⻓时间后再取回消息。
  • 流量削锋:当上下游系统处理能⼒存在差距的时候,利⽤消息队列做⼀个通⽤的”载体”,在下游有能⼒处理的 时候,再进⾏分发与处理。
  • ⽇志处理:⽇志处理是指将消息队列⽤在⽇志处理中,⽐如 Kafka 的应⽤,解决⼤量⽇志传输的问题。
  • 消息通讯:消息队列⼀般都内置了⾼效的通信机制,因此也可以⽤在纯的消息通讯,⽐如实现点对点消息队 列,或者聊天室等。
  • 消息⼴播:如果没有消息队列,每当⼀个新的业务⽅接⼊,我们都要接⼊⼀次新接⼝。有了消息队列,我们只 需要关⼼消息是否送达了队列,⾄于谁希望订阅,是下游的事情,⽆疑极⼤地减少了开发和联调的⼯作量。

三:常⽤消息队列

由于官⽅社区现在对 ActiveMQ 5.x 维护越来越少,较少在⼤规模吞吐的场景中使⽤,所以我们主要讲解 Kafka、 RabbitMQ 和 RocketMQ。

3.1 Kafka

Apache Kafka 最初由 LinkedIn 公司基于独特的设计实现为⼀个分布式的提交⽇志系统,之后成为 Apache 项⽬的 ⼀部分,号称⼤数据的杀⼿锏,在数据采集、传输、存储的过程中发挥着举⾜轻重的作⽤。

它是⼀个分布式的,⽀持多分区、多副本,基于 Zookeeper 的分布式消息流平台,它同时也是⼀款开源的基于发 布订阅模式的消息引擎系统。

3.1.1 重要概念:

  • 主题(Topic):消息的种类称为主题,可以说⼀个主题代表了⼀类消息,相当于是对消息进⾏分类,主题就 像是数据库中的表。
  • 分区(partition):主题可以被分为若⼲个分区,同⼀个主题中的分区可以不在⼀个机器上,有可能会部署 在多个机器上,由此来实现 kafka 的伸缩性。
  • 批次:为了提⾼效率, 消息会分批次写⼊ Kafka,批次就代指的是⼀组消息。
  • 消费者群组(Consumer Group):消费者群组指的就是由⼀个或多个消费者组成的群体。
  • Broker: ⼀个独⽴的 Kafka 服务器就被称为 broker,broker 接收来⾃⽣产者的消息,为消息设置偏移量,并 提交消息到磁盘保存。
  • Broker 集群:broker 集群由⼀个或多个 broker 组成。
  • 重平衡(Rebalance):消费者组内某个消费者实例挂掉后,其他消费者实例⾃动重新分配订阅主题分区的 过程。

3.1.2 Kafka 架构

⼀个典型的 Kafka 集群中包含 Producer、broker、Consumer Group、Zookeeper 集群。

Kafka 通过 Zookeeper 管理集群配置,选举 leader,以及在 Consumer Group 发⽣变化时进⾏ rebalance

Producer 使⽤ push 模式将消息发布到 broker,Consumer 使⽤ pull 模式从 broker 订阅并消费消息。

image.png

3.1.3 Kafka ⼯作原理

消息经过序列化后,通过不同的分区策略,找到对应的分区。

相同主题和分区的消息,会被存放在同⼀个批次⾥,然后由⼀个独⽴的线程负责把它们发到 Kafka Broker 上。

image.png

分区的策略包括顺序轮询、随机轮询和 key hash 这 3 种⽅式,那什么是分区呢?

分区是 Kafka 读写数据的最⼩粒度,⽐如主题 A 有 15 条消息,有 5 个分区,如果采⽤顺序轮询的⽅式,15 条消 息会顺序分配给这 5 个分区,后续消费的时候,也是按照分区粒度消费。

image.png

由于分区可以部署在多个不同的机器上,所以可以通过分区实现 Kafka 的伸缩性,⽐如主题 A 的 5 个分区,分别 部署在 5 台机器上,如果下线⼀台,分区就变为 4。

Kafka 消费是通过消费群组完成,同⼀个消费者群组,⼀个消费者可以消费多个分区,但是⼀个分区,只能被⼀个 消费者消费。

image.png

如果消费者增加,会触发 Rebalance,也就是分区和消费者需要重新配对。

不同的消费群组互不⼲涉,⽐如下图的 2 个消费群组,可以分别消费这 4 个分区的消息,互不影响。

image.png

3.2 RocketMQ

RocketMQ 是阿⾥开源的消息中间件,它是纯 Java 开发,具有⾼性能、⾼可靠、⾼实时、适合⼤规模分布式系统 应⽤的特点。

RocketMQ 思路起源于 Kafka,但并不是 Kafka 的⼀个 Copy,它对消息的可靠传输及事务性做了优化,⽬前在阿 ⾥集团被⼴泛应⽤于交易、充值、流计算、消息推送、⽇志流式处理、binglog 分发等场景。

3.2.1 重要概念

  • Name 服务器(NameServer):充当注册中⼼,类似 Kafka 中的 Zookeeper。
  • Broker: ⼀个独⽴的 RocketMQ 服务器就被称为 broker,broker 接收来⾃⽣产者的消息,为消息设置偏移 量。
  • 主题(Topic):消息的第⼀级类型,⼀条消息必须有⼀个 Topic。
  • ⼦主题(Tag):消息的第⼆级类型,同⼀业务模块不同⽬的的消息就可以⽤相同 Topic 和不同的 Tag 来标 识。
  • 分组(Group):⼀个组可以订阅多个 Topic,包括⽣产者组(Producer Group)和消费者组(Consumer Group)。
  • 队列(Queue):可以类⽐ Kafka 的分区 Partition。

3.2.2 RocketMQ ⼯作原理

RockerMQ 中的消息模型就是按照主题模型所实现的,包括 Producer Group、Topic、Consumer Group 三个⻆ ⾊。

为了提⾼并发能⼒,⼀个 Topic 包含多个 Queue,⽣产者组根据主题将消息放⼊对应的 Topic,下图是采⽤轮询 的⽅式找到⾥⾯的 Queue。

RockerMQ 中的消费群组和 Queue,可以类⽐ Kafka 中的消费群组和 Partition:不同的消费者组互不⼲扰,⼀个 Queue 只能被⼀个消费者消费,⼀个消费者可以消费多个 Queue。

消费 Queue 的过程中,通过偏移量记录消费的位置。

image.png

3.3 RocketMQ 架构

RocketMQ 技术架构中有四⼤⻆⾊ NameServer、Broker、Producer 和 Consumer,下⾯主要介绍 Broker。

Broker ⽤于存放 Queue,⼀个 Broker 可以配置多个 Topic,⼀个 Topic 中存在多个 Queue。

如果某个 Topic 消息量很⼤,应该给它多配置⼏个 Queue,并且尽量多分布在不同 broker 上,以减轻某个 broker 的压⼒。Topic 消息量都⽐较均匀的情况下,如果某个 broker 上的队列越多,则该 broker 压⼒越⼤。

image.png

简单提⼀下,Broker 通过集群部署,并且提供了 master/slave 的结构,salve 定时从 master 同步数据(同步刷 盘或者异步刷盘),如果 master 宕机,则 slave 提供消费服务,但是不能写⼊消息。

看到这⾥,⼤家应该可以发现,RocketMQ 的设计和 Kafka 真的很像!

3.4 RabbitMQ

RabbitMQ 2007 年发布,是使⽤ Erlang 语⾔开发的开源消息队列系统,基于 AMQP 协议来实现。

AMQP 的主要特征是⾯向消息、队列、路由、可靠性、安全。AMQP 协议更多⽤在企业系统内,对数据⼀致性、 稳定性和可靠性要求很⾼的场景,对性能和吞吐量的要求还在其次。

3.4.1 重要概念

  • 信道(Channel):消息读写等操作在信道中进⾏,客户端可以建⽴多个信道,每个信道代表⼀个会话任 务。
  • 交换器(Exchange):接收消息,按照路由规则将消息路由到⼀个或者多个队列;如果路由不到,或者返回 给⽣产者,或者直接丢弃。
  • 路由键(RoutingKey):⽣产者将消息发送给交换器的时候,会发送⼀个 RoutingKey,⽤来指定路由规 则,这样交换器就知道把消息发送到哪个队列。
  • 绑定(Binding):交换器和消息队列之间的虚拟连接,绑定中可以包含⼀个或者多个 RoutingKey。

3.4.2 RabbitMQ ⼯作原理

AMQP 协议模型由三部分组成:⽣产者、消费者和服务端,执⾏流程如下:

  1. ⽣产者是连接到 Server,建⽴⼀个连接,开启⼀个信道。
  2. ⽣产者声明交换器和队列,设置相关属性,并通过路由键将交换器和队列进⾏绑定。
  3. 消费者也需要进⾏建⽴连接,开启信道等操作,便于接收消息。
  4. ⽣产者发送消息,发送到服务端中的虚拟主机。
  5. 虚拟主机中的交换器根据路由键选择路由规则,发送到不同的消息队列中。
  6. 订阅了消息队列的消费者就可以获取到消息,进⾏消费。

image.png

常用交换器:

常⽤交换器 RabbitMQ 常⽤的交换器类型有 direct、topic、fanout、headers 四种

四:消息队列选型

  • Kafka:追求⾼吞吐量,⼀开始的⽬的就是⽤于⽇志收集和传输,适合产⽣⼤量数据的互联⽹服务的数据收集 业务,⼤型公司建议可以选⽤,如果有⽇志采集功能,肯定是⾸选 kafka。
  • RocketMQ:天⽣为⾦融互联⽹领域⽽⽣,对于可靠性要求很⾼的场景,尤其是电商⾥⾯的订单扣款,以及业 务削峰,在⼤量交易涌⼊时,后端可能⽆法及时处理的情况。
  • RoketMQ 在稳定性上可能更值得信赖,这些业 务场景在阿⾥双 11 已经经历了多次考验,如果你的业务有上述并发场景,建议可以选择 RabbitMQ:结合 erlang 语⾔本身的并发优势,性能较好,社区活跃度也⽐较⾼,但是不利于做⼆次开发和 维护,不过 RabbitMQ 的社区⼗分活跃,可以解决开发过程中遇到的 bug。如果你的数据量没有那么⼤,⼩ 公司优先选择功能⽐较完备的 RabbitMQ。
  • ActiveMQ:官⽅社区现在对 ActiveMQ 5.x 维护越来越少,较少在⼤规模吞吐的场景中使⽤。