上次我们讲解了 RocketMQ 的相关概念,详情可以查看:RocketMQ:能够抵挡双十一的中间件是怎么设计的?
通过上篇文章我们可以了解到 RocketMQ 的相关构成以及概念。那还记不记得在上篇文章中介绍 消费者组
概念的时候提到:每个消费者组之间的消息互不干扰,也就是 A 消费者组的 a 消费了消息,这并不会影响到 B 消费者组中的 b 消费这条消息。
那这是怎么做到的呢?是每多一个消费者组就拷贝一份消息出来吗?乍一看好像确实可行奥,但是!你有没有想过一种可能,如果我有 1000 个消费者组呢?拷贝 1000 份?那这样的话显然就不是很合理了。
那到底是怎么做到的呢?
这就不得不提到实现消息队列的两种模式了:
- 队列模式
- 发布 – 订阅
队列模式
首先,一提到队列,能想到什么?对嘛,正是 FIFO 啦(先进先出)。开动脑筋仔细想想,在日常生活中有那些场景可以满足 FIFO 呢?
嗯~就比如我们在学校食堂打饭,又或者在超市购物结账的时候,其实也都是队列的场景。
![图片[1]-消息队列都有什么模型,RocketMQ又采用的哪种?-编程社](https://cos.bianchengshe.com/wp-content/uploads/2024/07/image-21.png?imageMogr2/format/webp/interlace/1/quality/100)
看这张图就可以很清楚的理解啦。
对应到消息队列的场景中其实也就是换汤不换药:
![图片[2]-消息队列都有什么模型,RocketMQ又采用的哪种?-编程社](https://cos.bianchengshe.com/wp-content/uploads/2024/07/image-22.png?imageMogr2/format/webp/interlace/1/quality/100)
针对于消息队列的特性,被消费完的消息也就出队(被移除)了。这种模式下,一条消息只能被一个消费者消费。
嘶~好像也没问题啊。是的,在早期的时候确实没什么问题,但是随着业务的发展,这种方案就不是很好了。看一张图就可以明白了。
![图片[3]-消息队列都有什么模型,RocketMQ又采用的哪种?-编程社](https://cos.bianchengshe.com/wp-content/uploads/2024/07/image-23.png?imageMogr2/format/webp/interlace/1/quality/100)
Boss 的意思是大家都加个班、都开个会。但是此时员工臭香并没有拿到加班的消息,肯定会不高兴的是吧。
那这就和刚刚说的多个消费者都想消费同一条消息互相违背了呀,毕竟他们也都想加班对吧。
那这种场景就没办法实现了嘛?想什么呢,当然可以了,copy 呗,每个员工都对应一条队列不就行了?
![图片[4]-消息队列都有什么模型,RocketMQ又采用的哪种?-编程社](https://cos.bianchengshe.com/wp-content/uploads/2024/07/image-24.png?imageMogr2/format/webp/interlace/1/quality/100)
:这种“好事”怎么能少得了你们呢是吧,安排上,都安排上。
这种是可以解决问题不假,但是带来的另一个问题就是:消息冗余!一模一样的消息,其实完全可以只有一份就够了呀。那就在带大家认识认识新模式:发布订阅!
发布 – 订阅
什么是 发布 - 订阅
呢?
发布我们很容易就可以联想到发布消息呗,那订阅是订阅什么呢?上文中我们提到了一个概念:Topic。对咯,就是订阅 Topic。
比如说往 Topic-体育 中发布了消息,那么订阅了 Topic-体育 的消费者都可以收到这条消息。
这么说发布订阅确实可以解决上述的问题,那么到底是怎么解决的呢?
这里需要引入一个概念:消息位置(offset)。其实可以理解为数组的下标。
每个消费者只需要记录自己消费消息的位置就好了,每消费一条,offset 就 +1。这样就可以满足多个消费者消费同一条消息了。
![图片[5]-消息队列都有什么模型,RocketMQ又采用的哪种?-编程社](https://cos.bianchengshe.com/wp-content/uploads/2024/07/image-25.png?imageMogr2/format/webp/interlace/1/quality/100)
消费者每消费完一个消息之后 offset 加 1,并不会影响到其他消费者消费消息。
那如果我并不是只有一个消费者呢?我需要有多个消费者屈订阅同一个 Topic 中的内容,而且这个消费者组同样也不可以重复消费,怎么办?
针对这个问题,这里又需要引入一个新的概念了,叫做:队列,这个只是叫做队列而已,比如在 kafka 中叫做分区,只是一个名字而已。
![图片[6]-消息队列都有什么模型,RocketMQ又采用的哪种?-编程社](https://cos.bianchengshe.com/wp-content/uploads/2024/07/image-26.png?imageMogr2/format/webp/interlace/1/quality/100)
可以清楚的看到,一个 Topic 下可以有 2 个或多个 队列。
那也就说明可以给消费者组中的每一个消费者对应一个不同的队列就可以了,就完美解决上述问题。
![图片[7]-消息队列都有什么模型,RocketMQ又采用的哪种?-编程社](https://cos.bianchengshe.com/wp-content/uploads/2024/07/image-27.png?imageMogr2/format/webp/interlace/1/quality/100)
那如果我的消费者组又新增了一名新的消费者,也就是现在变成 3 个消费者了,又怎么办呢?
答案是继续加呗。同样如果消费者只有 2 个,但是队列缺有 3 个,怎么办呢?那就辛苦其中一位消费者同时消费 2 条队列中的消息就好了啊。
斗胆猜测一下,有没有人有疑问:为什么会出现 2 个消费者对应 3 条队列的场景呢?不应该是一一对应的嘛?
想法很好。但是这种情况是有滴,如果有一个消费者嘎了,是不是就有这种场景了呢?那这个消费者对应的这条队列的消息就直接丢弃吗?
这种设计的好处是不是就显现出来了?但是我想告诉你的是,这种设计的优点还没有完。
消息重复消费
假设一个场景,如果现在有一个消费者 A,现在已经消费了 10 条消息了,但是不小心把消费消息得到的结果给弄丢了。在队列模式中,已经消费的消息就出队了,显然这种在队列模式中是无法(很难)挽回的损失,但是在发布订阅模式中,丢了消费消息的结果,直接将自己的 offset 调整回一开始去重新消费消息不就好了?
消息跳跃消费
还是一样,我消费到第 10 条之后,不想消费第 11 条消息,那直接将 offset 修改为 12 就可以了。
所以,这种设计还是很灵活的。
那生产者怎么知道应该将消息发送给哪个队列呢?队列那么多。
- 轮询
- 根据不同类型发送
轮询就是生产者将第 1 条消息发送给队列 1;第 2 条消息发送给队列 2…从而实现每个队列中的消息数量基本上一致。
根据不同类型发送就是,可以根据我们上文提到的 Tag 去区分消息的不同类型,比如将有关于乒乓球的消息全部发送到队列 1 中,将有关羽毛球的全部发送到队列 2 中。
以上就是消息队列的两种模式介绍。在我们讲解的 RocketMQ 还有 kafka 中是使用的都是 发布订阅
模式,而在 RabbitMQ 中使用的是队列模式。
暂无评论内容