消息队列:主题和队列有什么区别?

队列模型 与 发布-订阅模型

队列是“先进先出”的,早期的队列,就是按照“队列”的数据结构设计的。生产者发消息就是入队操作,消费者收消息就是出队操作,服务端存放消息的容器自然成为**“队列”**。

这就是最初的一种消息模型——队列模型

  • 如果有多个生产者往同一个队列里面发送消息,这个队列中可以消费到的消息,就是这些生产者生产的所有消息的合集。消息的顺序就是这些生产者发送消息的自然顺序。
  • 如果有多个消费者接收同一个队列的消息,这些消费者之间实际上是竞争的关系,每个消费者只能收到队列中的一部分消息,也就是说任何一条消息只能被其中的一个消费者收到。
  • 如果需要将一份消息数据分发给多个消费者,要求每个消费者都能收到全量的消息。这个时候,单个队列就满足不了需求,一个可行的解决方式是,为每个消费者创建一个单独的队列,让生产者发送多份。

显然这是个比较蠢的做法,同样的一份消息数据被复制到多个队列中会浪费资源,更重要的是,生产者必须知道有多少个消费者。为每个消费者单独发送一份消息,这实际上违背了消息队列“解耦”这个设计初衷。

为了解决上述问题,演化出了另外一种消息模型——“发布-订阅模型”。
在这里插入图片描述

图源http://learn.lianglianglee.com/专栏/消息队列高手课/03 消息模型:主题和队列有什么区别?.md

发布者将消息发送到主题中,订阅者在接收消息之前需要先“订阅主题”。“订阅”在这里既是一个动作,同时还可以认为是主题在消费时的一个逻辑副本,在每份订阅中,订阅者都可以接收到主题的所有消息。

队列模型 和 发布-订阅模型 最大的区别其实就是:一份消息数据能不能被消费多次的问题。

RabbitMQ的消息模型

在RabbitMQ中,Exchange位于生产者和队列之间,生产者并不关心将消息发送给哪个队列,二十将消息发送给Exchange,由Exchange上配置的策略来来决定将消息投递到哪些队列中。

在这里插入图片描述

同一份消息如果被多个消费者来消费,需要配置Exchange将消息发送到多个队列,每个队列中都存放一份完整的消息数据,可以为一个消费者提供消息服务。

RocketMQ的消息模型

RocketMQ使用的消息模型是标准的发布-订阅模型。而RocketMQ室友Queue的概念的,这里我们先从消息队列的消费机制说起:

几乎所有的消息队列产品都使用“请求-确认“机制,确保消息不会在传递过程中由于网络活服务器故障丢失。

  • 生产端:生产者先将消息发送给服务端,也就是 Broker,服务端在收到消息并将消息写入主题或者队列中后,会给生产者发送确认的响应。如果生产者没有收到服务端的缺人或者收到失败的响应,则会重新发送消息。
  • 消费端:消费者在收到消息并完成自己的消费业务逻辑后,也会给服务端发送消费成功的确认,服务端只有收到消费确认后,才认为一条消息被成功消费,否则它会给消费者重新发送这条消息,直到收到对应的消费成功确认。

这个机制有一个很大的缺点:为了保证消息的有序性,在某一条消息被成功消费之前,下一条消息是不能被消费的。也就是说,每个主题在任意时刻,至多只能有一个消费者实例在进行消费,那就没法通过水平扩展消费者的数量来提升消费端总体的消费性能。

为了解决这个问题,RocketMQ在主题下面增加了队列的概念:

每个主题包含多个队列,通过多个队列来实现多实例并行生产和消费。RocketMQ 只在队列上保证消息的有序性,主题层面是无法保证消息的严格顺序的。

订阅者的概念是通过消费组(Consumer Group)来体现的。每个消费组都消费主题中一份完整的消息,不同消费组之间消费进度彼此不受影响。

消费组中包含多个消费者,同一个组内的消费者是竞争消费的关系,每个消费者负责消费组内的一部分消息。如果一条消息被消费者 Consumer1 消费了,那同组的其他消费者就不会再收到这条消息。

Kafka的消息模型

Kafka的消息模型和RocketMQ是完全一样的,唯一的区别是,在Kafka中,“Queue”的名字是“分区”。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐