RocketMQ - 消息队列

概述

思考:不同的消费组之间可以全量消费同Topic的消息,它们之间的消费进度又可以不一样,是Broker将消息复制了多份吗?如果有上百个消费组,岂不是要复制上百份一模一样的消息?这样合理吗?你有更好的解决方式吗?

引出今天的主角:消息队列。

消息队列

队列是一种数据结构,遵循着先进先出的原则,类似于排队打饭。

按照队列的特性,消息被消费了等于出队,即从队列上被移除了,那么每条消息只会被一个消费者消费,因此消费者之间是 竞争关系

这种模式就是队列模式,符合初期的设计,一个消息被消费后,自然应该被移除,避免影响后续消息的到达。

这样的设计在早期没问题,但是随着互联网演进,系统的复杂性不断提升,随之而来的需求也更加繁琐。

就像一个消息可能有很多消费者都感兴趣,但是他们之间又不是竞争消费的关系,即 这些消费者都想消费所有的消息

这时队列模式是不是就不太合适了?因为队列的设计是消息被消费了就出队,而出队了如何再被别的消费者消费呢?

很自然地我们会想到把消息复制成多份,也就是多队列!

这样一条消息被冗余到多个队列中,每个队列都包含全量的消息,这就满足了这些不是竞争关系的消费者们的需求。

但这样的方式对存储就不是很友好了,因为随着消费者们的增加,队列会越来越多, 冗余的消息也就越来越多。于是就逐步演进,出现了发布-订阅模式。

发布-订阅模式

引入(offset)的概念,原始述求是消息可以被多个消费者消费,那么只需维护每个消费者已经消费到的位置,每当消费者消费一条消息,消费位置就+1,然后消费者根据记录的消息位置去消费对应的数据即可。就跟遍历数组一样,下标+1来访问后面的数据。 消费位置 这样就能满足不同消费者消费同一条消息,且不影响他们之间的消费进度的需求。

  • Topic-足球-小A-2
  • Topic-足球-小B-3
  • Topic-篮球-小A-1
  • Topic-篮球-小B-2

同一个消费组内的消费者如何消费消息呢?让他们竞争同一个消费位置吗?那岂不是需要等上一个消费者消费完了,组内其他消费者才能消费 下一条消息?

引入RocketMQ的队列概念,在Kafka里叫作分区

可以看到,发往一个Topic的消息,实际上不是在一个队列里,而是分布在多个队列中。

这样属于同一个消费组的消费者们可以专门负责主题里面的一个队列,比如下图:

记录消费点位方式的转变:Topic-消费者-消费位置 ——> Topic-消费组-队列(-队列里的位置)

随着Topic对应的队列变多,可以通过新增消费者成员来降低消费压力。

这个方案非常灵活,加队列同时加消费者的处理方式就是消息堆积的一个解决办法之一

企业级消息队列实现的发布-订阅模式的核心原理**:即Topic下分队列(分区)/然后维护每个消费组在每个Topic下每个队列的消息位置,以消息位置(offset)来控制消费的进度。**

消息位置的灵活性不仅仅能区分不同消费组或者说消费者们的消费进度,还能实现重复消费或者跳过部分消息不消费的功能。

一个Topic里面有多个队列,那么生产者怎么知道要发到哪个队列?

一个很简单的办法就是轮询,比如生产者-A,要往Topic-篮球发送消息,那么第一条发给队列-1,第二条发给队列-2,第三条发给队列-3,第四条发给队列-1,第五条发给队列-2...如此往复

这样每个队列的消息量会很平均,对应的消费者的工作量也会均衡。当然也可以指定发往某个队列。