一. 队列 To 主题

    程序员​常听这样一句话:好的架构不是设计出来的,而是演变出来的。

    ​最初的消息队列就是一个严格意义上的队列。它的模型如下:

 

    ​如果有多个生产者往同一个队列里发送消息,那么消费者消费的就是所有生产者消息的合集,消息的顺序就是生产者发送消息的自然顺序。如果有多个消费者接收同一个消息队列,那么这些消费者是竞争关系,每个消费者接收到的都是队列中的一部分。

    ​如果需要将同一个消息发送到不同的队列,每个队列都接受到全量消息,那么做法只能是创建相同数目的队列,并让生产者王多个队列中发送数据。

    ​显然这样的设计会有一些问题,相同的消息放入不同的队列中会造成资源浪费。而且更重要的是,每个生产者都需要知道有多少个消费者存在,这违反了消息队列“解耦”的初衷。

 

    ​为了解决这一问题,引入了发布-订阅模型。

 

    ​在发布订阅模型中,消息的发送方为发布者(Publisher),消息的消费方为订阅者(Subscriber),消息存放的容器成为主题(Topic)。发布者将消息发布到主题中,所有订阅这个主题的订阅者都会收到这条消息,每份订阅都会收到这个主题的所有消息。

    实际上,如果只有一个订阅者,那么发布订阅模型和队列模型就基本是一样的了。也就是说,在功能层面,发布订阅模型可以兼容队列模型。它们俩者的最大的区别就在于,一条消息能不能被消费多次。

    ​二. 主流MQ的模型

    ​1. RabbitMQ

    ​    ​如今大多数消息队列产品的消息模型都支持发布订阅模型,不过RabbitMQ是个例外。上一章我们介绍了RabbitMQ很有特点的一个模块Exchange,这个模块可以将消息发送者发出的消息根据路由规则发送至不同的队列,这样RabbitMQ就实现了基于队列的、同一消息可以供多个消费者消费的功能。

    ​2. RocketMQ

    ​    ​RocketMQ是标准的发布订阅模型,它和上边给出的发布订阅模型是一样的。

    ​    ​不过RocketMQ也有队列(Queue)的概念。那么队列在RocketMQ中起到什么作用呢?

    ​    ​几乎所有的消息队列产品都使用“请求-确认”机制,确保消息在网络传输中不会因为网络或者服务器故障丢失。具体的方法也很简单,生产者将消息发送至服务端,也就是Broker,服务端只有收到消息后将消息写入队列或者主题,并向生产者发送确认的响应。如果生产者没有收到确认响应就会重新发送消息。在消费端,消费者在收到消息并完成自己的消费业务逻辑后会向服务端发送消费成功的确认。服务端收到确认后才会认为这条消息被成功消费,否则服务端会重复发送这条消息,直到收到确认消息。

    ​    ​这个确认机制很好的保证了消息的可靠性传输。但是,引入这个机制在消费端带来了一个不小的问题,当一条消息没有被成功消费前,其他消息是不会被消费的,否则会出现消息空洞,违背了有序性的原则。也就是说,一个主题在任意时刻至多有能有一个消费者实例在消费,那就没法过水平扩容消费者实例的数量来提升消费端总体的消费性能。为了解决这个问题,RocketMQ在主题中增加了队列的概念。

    ​    ​每个主题有多个队列,通过队列来实现多实例并行生产和消费。值得一提的是,RocketMQ只保证队列上消息的有序性,不保证主题层面消息的有序性。

    ​    ​RocketMQ中订阅者以消费组的形式存在,每个消费组都消费一个主题中完整的消息,不同消费组之间的进度不受彼此影响。一个消费组内有多个消费者,这些消费者是组内竞争关系,它们共享一份完整的消息,当一条消息被其中要给消费者消费了以后,就不会再被其他消费者消费了。以上就是队列再RocketMQ中的作用。

    ​    ​在Topic消费过程中,由于消息需要被不同的消费组消费,所以当一个消费组消费完一个消息时,主题不会将这条消息删除,而是维护了各个消费组在主题队列中的消费位置。

    ​    ​

    ​3. kafka

​    ​    ​kafka的模型和RocketMQ是完全一样的,只是RocketMQ中队列的概念在kafka中叫做分区Partition。

posted on 2019-10-11 02:03  Man-YAN  阅读(528)  评论(0)    收藏  举报