选Redis做MQ的人,是脑子里缺根弦儿吗?

目录
 

  • 一、前情提示
  • 二、unack消息的积压问题
  • 三、如何解决unack消息的积压问题
  • 四、高并发场景下的内存溢出问题
  • 五、低吞吐量问题
  • 六、合理设置prefetch count
  • 七、阶段性总结
 
【选Redis做MQ的人,是脑子里缺根弦儿吗?】一、前情提示
上一篇文章:《RocketMQ消息中间件用起来真的可靠吗?》,我们分析了ack机制的底层实现原理(delivery tag机制),还有消除处理失败时的nack机制如何触发消息重发 。
通过这个,已经让大家进一步对消费端保证数据不丢失的方案的理解更进一层了 。
这篇文章,我们将会对ack底层的delivery tag机制进行更加深入的分析,让大家理解的更加透彻一些 。
面试时,如果被问到消息中间件数据不丢失问题的时候,可以更深入到底层,给面试官进行分析 。
二、unack消息的积压问题
首先,我们要给大家介绍一下RabbitMQ的prefetch count这个概念 。
大家看过上篇文章之后应该都知道了,对每个channel(其实对应了一个消费者服务实例,你大体可以这么来认为),RabbitMQ投递消息的时候,都是会带上本次消息投递的一个delivery tag的,唯一标识一次消息投递 。
然后,我们进行ack时,也会带上这个delivery tag,基于同一个channel进行ack,ack消息里会带上delivery tag让RabbitMQ知道是对哪一次消息投递进行了ack,此时就可以对那条消息进行删除了 。
大家先来看一张图,帮助大家回忆一下这个delivery tag的概念 。
选Redis做MQ的人,是脑子里缺根弦儿吗?

文章插图
 
所以大家可以考虑一下,对于每个channel而言(你就认为是针对每个消费者服务实例吧,比如一个仓储服务实例),其实都有一些处于unack状态的消息 。
比如RabbitMQ正在投递一条消息到channel,此时消息肯定是unack状态吧?
然后仓储服务接收到一条消息以后,要处理这条消息需要耗费时间,此时消息肯定是unack状态吧?
同时,即使你执行了ack之后,你要知道这个ack他默认是异步执行的,尤其如果你开启了批量ack的话,更是有一个延迟时间才会ack的,此时消息也是unack吧?
那么大家考虑一下,RabbitMQ他能够无限制的不停给你的消费者服务实例推送消息吗?
明显是不能的,如果RabbitMQ给你的消费者服务实例推送的消息过多过快,比如都有几千条消息积压在某个消费者服务实例的内存中 。
那么此时这几千条消息都是unack的状态,一直积压着,是不是有可能会导致消费者服务实例的内存溢出?内存消耗过大?甚至内存泄露之类的问题产生?
所以说,RabbitMQ是必须要考虑一下消费者服务的处理能力的 。
大家看看下面的图,感受一下如果消费者服务实例的内存中积压消息过多,都是unack的状态,此时会怎么样 。
选Redis做MQ的人,是脑子里缺根弦儿吗?

文章插图
 
三、如何解决unack消息的积压问题
正是因为这个原因,RabbitMQ基于一个prefetch count来控制这个unack message的数量 。
你可以通过 “channel.basicQos(10)” 这个方法来设置当前channel的prefetch count 。
举个例子,比如你要是设置为10的话,那么意味着当前这个channel里,unack message的数量不能超过10个,以此来避免消费者服务实例积压unack message过多 。
这样的话,就意味着RabbitMQ正在投递到channel过程中的unack message,以及消费者服务在处理中的unack message,以及异步ack之后还没完成ack的unack message,所有这些message加起来,一个channel也不能超过10个 。
如果你要简单粗浅的理解的话,也大致可以理解为这个prefetch count就代表了一个消费者服务同时最多可以获取多少个message来处理 。所以这里也点出了prefetch这个单词的意思 。
prefetch就是预抓取的意思,就意味着你的消费者服务实例预抓取多少条message过来处理,但是最多只能同时处理这么多消息 。
如果一个channel里的unack message超过了prefetch count指定的数量,此时RabbitMQ就会停止给这个channel投递消息了,必须要等待已经投递过去的消息被ack了,此时才能继续投递下一个消息 。
老规矩,给大家上一张图,我们一起来看看这个东西是啥意思 。
选Redis做MQ的人,是脑子里缺根弦儿吗?

文章插图
 
四、高并发场景下的内存溢出问题


推荐阅读