您当前的位置:首页 > 电脑百科 > 数据库 > Redis

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

时间:2022-12-29 13:46:17  来源:今日头条  作者:石杉的架构笔记

目录

 

  • 一、前情提示
  • 二、unack消息的积压问题
  • 三、如何解决unack消息的积压问题
  • 四、高并发场景下的内存溢出问题
  • 五、低吞吐量问题
  • 六、合理设置prefetch count
  • 七、阶段性总结

 

一、前情提示

上一篇文章:《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的概念。


 

所以大家可以考虑一下,对于每个channel而言(你就认为是针对每个消费者服务实例吧,比如一个仓储服务实例),其实都有一些处于unack状态的消息。

比如RabbitMQ正在投递一条消息到channel,此时消息肯定是unack状态吧?

然后仓储服务接收到一条消息以后,要处理这条消息需要耗费时间,此时消息肯定是unack状态吧?

同时,即使你执行了ack之后,你要知道这个ack他默认是异步执行的,尤其如果你开启了批量ack的话,更是有一个延迟时间才会ack的,此时消息也是unack吧?

那么大家考虑一下,RabbitMQ他能够无限制的不停给你的消费者服务实例推送消息吗

明显是不能的,如果RabbitMQ给你的消费者服务实例推送的消息过多过快,比如都有几千条消息积压在某个消费者服务实例的内存中。

那么此时这几千条消息都是unack的状态,一直积压着,是不是有可能会导致消费者服务实例的内存溢出?内存消耗过大?甚至内存泄露之类的问题产生?

所以说,RabbitMQ是必须要考虑一下消费者服务的处理能力的。

大家看看下面的图,感受一下如果消费者服务实例的内存中积压消息过多,都是unack的状态,此时会怎么样。


 

三、如何解决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了,此时才能继续投递下一个消息。

老规矩,给大家上一张图,我们一起来看看这个东西是啥意思。


 

四、高并发场景下的内存溢出问题

好!现在大家对ack机制底层的另外一个核心机制:prefetch机制也有了一个深刻的理解了。

此时,咱们就应该来考虑一个问题了。就是如何来设置这个prefetch count呢?这个东西设置的过大或者过小有什么影响呢

其实大家理解了上面的图就很好理解这个问题了。

假如说我们把prefetch count设置的很大,比如说3000,5000,甚至100000,就这样特别大的值,那么此时会如何呢?

这个时候,在高并发大流量的场景下,可能就会导致消费者服务的内存被快速的消耗掉。

因为假如说现在MQ接收到的流量特别的大,每秒都上千条消息,而且此时你的消费者服务的prefetch count还设置的特别大,就会导致可能一瞬间你的消费者服务接收到了达到prefetch count指定数量的消息。

打个比方,比如一下子你的消费者服务内存里积压了10万条消息,都是unack的状态,反正你的prefetch count设置的是10万。

那么对一个channel,RabbitMQ就会最多容忍10万个unack状态的消息,在高并发下也就最多可能积压10万条消息在消费者服务的内存里。

那么此时导致的结果,就是消费者服务直接被击垮了,内存溢出,OOM,服务宕机,然后大量unack的消息会被重新投递给其他的消费者服务,此时其他消费者服务一样的情况,直接宕机,最后造成雪崩效应

所有的消费者服务因为扛不住这么大的数据量,全部宕机。

大家来看看下面的图,自己感受一下现场的氛围。


 

五、低吞吐量问题

那么如果反过来呢,我们要是把prefetch count设置的很小会如何呢

比如说我们把prefetch count设置为1?此时就必然会导致消费者服务的吞吐量极低。因为你即使处理完一条消息,执行ack了也是异步的。

给你举个例子,假如说你的prefetch count = 1,RabbitMQ最多投递给你1条消息处于unack状态。

此时比如你刚处理完这条消息,然后执行了ack的那行代码,结果不幸的是,ack需要异步执行,也就是需要100ms之后才会让RabbitMQ感知到。

那么100ms之后RabbitMQ感知到消息被ack了,此时才会投递给你下一条消息!

这就尴尬了,在这100ms期间,你的消费者服务是不是啥都没干啊?

这不就直接导致了你的消费者服务处理消息的吞吐量可能下降10倍,甚至百倍,千倍,都有这种可能!

大家看看下面的图,感受一下低吞吐量的现场。


 

六、合理的设置prefetch count

所以鉴于上面两种极端情况,RabbitMQ官方给出的建议是prefetch count一般设置在100~300之间。

也就是一个消费者服务最多接收到100~300个message来处理,允许处于unack状态。

这个状态下可以兼顾吞吐量也很高,同时也不容易造成内存溢出的问题。

但是其实在我们的实践中,这个prefetch count大家完全是可以自己去压测一下的。

比如说慢慢调节这个值,不断加大,观察高并发大流量之下,吞吐量是否越来越大,而且观察消费者服务的内存消耗,会不会OOM、频繁FullGC等问题。

七、阶段性总结

其实通过最近几篇文章,基本上已经把消息中间件的消费端如何保证数据不丢失这个问题剖析的较为深入和透彻了。

如果你是基于RabbitMQ来做消息中间件的话,消费端的代码里,必须考虑三个问题:手动ack、处理失败的nack、prefetch count的合理设置

这三个问题背后涉及到了各种机制:

 

  • 自动ack机制
  • delivery tag机制
  • ack批量与异步提交机制
  • 消息重发机制
  • 手动nack触发消息重发机制
  • prefetch count过大导致内存溢出问题
  • prefetch count过小导致吞吐量过低

 

这些底层机制和问题,咱们都一步步分析清楚了。

所以到现在,单论消费端这块的数据不丢失技术方案,相信大家在面试的时候就可以有一整套自己的理解和方案可以阐述了。



Tags:Redis   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
16个Redis常见使用场景总结
来源:blog.csdn.net/qq_39938758/article/details/105577370目录 缓存 数据共享分布式 分布式锁 全局ID 计数器 限流 位统计 购物车 用户消息时间线timeline 消息...【详细内容】
2024-04-11  Search: Redis  点击:(4)  评论:(0)  加入收藏
Linux获取Redis 性能指标方法
一、监控指标Ø 性能指标:PerformanceØ 内存指标: MemoryØ 基本活动指标:Basic activityØ 持久性指标: PersistenceØ 错误指标:Error二、监...【详细内容】
2024-04-11  Search: Redis  点击:(5)  评论:(0)  加入收藏
Redis与缓存一致性问题
缓存一致性问题是在使用缓存系统,如Redis时经常遇到的问题。当数据在原始数据源(如数据库)中发生变化时,如何确保缓存中的数据与数据源保持一致,是开发者需要关注的关键问题。一...【详细内容】
2024-04-11  Search: Redis  点击:(3)  评论:(0)  加入收藏
Redis 不再 “开源”,未来采用 SSPLv1 和 RSALv2 许可证
Redis 官方于21日宣布修改开源协议 —— 未来所有版本都将使用 “源代码可用” 的许可证 (source-available licenses)。具体来说,Redis 将不再遵循 BSD 3-Clause...【详细内容】
2024-03-27  Search: Redis  点击:(14)  评论:(0)  加入收藏
Redis“叛逃”开源,得罪了几乎所有人
内存数据库供应商Redis近日在开源界砸下了一块“巨石”。Redis即将转向双许可模式,并实施更为严格的许可条款。官方对此次变更的公告直截了当:从Redis 7.4版本开始,Redis将在Re...【详细内容】
2024-03-25  Search: Redis  点击:(10)  评论:(0)  加入收藏
如何使用 Redis 实现消息队列
Redis不仅是一个强大的内存数据存储系统,它还可以用作一个高效的消息队列。消息队列是应用程序间或应用程序内部进行异步通信的一种方式,它允许数据生产者将消息放入队列中,然...【详细内容】
2024-03-22  Search: Redis  点击:(18)  评论:(0)  加入收藏
Redis不再 “开源”
Redis 官方今日宣布修改开源协议 —— 未来所有版本都将使用 “源代码可用” 的许可证 (source-available licenses)。具体来说,Redis 将不再遵循 BSD 3-Clause 开...【详细内容】
2024-03-21  Search: Redis  点击:(11)  评论:(0)  加入收藏
在Redis中如何实现分布式锁的防死锁机制?
在Redis中实现分布式锁是一个常见的需求,可以通过使用Redlock算法来防止死锁。Redlock算法是一种基于多个独立Redis实例的分布式锁实现方案,它通过协调多个Redis实例之间的锁...【详细内容】
2024-02-20  Search: Redis  点击:(49)  评论:(0)  加入收藏
手动撸一个 Redis 分布式锁
大家好呀,我是楼仔。今天第一天开工,收拾心情,又要开始好好学习,好好工作了。对于使用 Java 的小伙伴,其实我们完全不用手动撸一个分布式锁,直接使用 Redisson 就行。但是因为这些...【详细内容】
2024-02-19  Search: Redis  点击:(40)  评论:(0)  加入收藏
工作中Redis有哪些好用的运维工具
工作中使用 Redis 时,如果大家公司没有专业运维,可能开发人员就会面临这些运维的工作,包括 Redis 的运行状态监控,数据迁移,主从集群、切片集群的部署和运维等等。本文我就从这三...【详细内容】
2024-02-06  Search: Redis  点击:(57)  评论:(0)  加入收藏
▌简易百科推荐
16个Redis常见使用场景总结
来源:blog.csdn.net/qq_39938758/article/details/105577370目录 缓存 数据共享分布式 分布式锁 全局ID 计数器 限流 位统计 购物车 用户消息时间线timeline 消息...【详细内容】
2024-04-11    书圈  Tags:Redis   点击:(4)  评论:(0)  加入收藏
Linux获取Redis 性能指标方法
一、监控指标Ø 性能指标:PerformanceØ 内存指标: MemoryØ 基本活动指标:Basic activityØ 持久性指标: PersistenceØ 错误指标:Error二、监...【详细内容】
2024-04-11  上海天正信息科技有限    Tags:Redis   点击:(5)  评论:(0)  加入收藏
Redis与缓存一致性问题
缓存一致性问题是在使用缓存系统,如Redis时经常遇到的问题。当数据在原始数据源(如数据库)中发生变化时,如何确保缓存中的数据与数据源保持一致,是开发者需要关注的关键问题。一...【详细内容】
2024-04-11  后端Q    Tags:Redis   点击:(3)  评论:(0)  加入收藏
Redis 不再 “开源”,未来采用 SSPLv1 和 RSALv2 许可证
Redis 官方于21日宣布修改开源协议 —— 未来所有版本都将使用 “源代码可用” 的许可证 (source-available licenses)。具体来说,Redis 将不再遵循 BSD 3-Clause...【详细内容】
2024-03-27  dbaplus社群    Tags:Redis   点击:(14)  评论:(0)  加入收藏
Redis“叛逃”开源,得罪了几乎所有人
内存数据库供应商Redis近日在开源界砸下了一块“巨石”。Redis即将转向双许可模式,并实施更为严格的许可条款。官方对此次变更的公告直截了当:从Redis 7.4版本开始,Redis将在Re...【详细内容】
2024-03-25    51CTO  Tags:Redis   点击:(10)  评论:(0)  加入收藏
如何使用 Redis 实现消息队列
Redis不仅是一个强大的内存数据存储系统,它还可以用作一个高效的消息队列。消息队列是应用程序间或应用程序内部进行异步通信的一种方式,它允许数据生产者将消息放入队列中,然...【详细内容】
2024-03-22  后端Q  微信公众号  Tags:Redis   点击:(18)  评论:(0)  加入收藏
Redis不再 “开源”
Redis 官方今日宣布修改开源协议 —— 未来所有版本都将使用 “源代码可用” 的许可证 (source-available licenses)。具体来说,Redis 将不再遵循 BSD 3-Clause 开...【详细内容】
2024-03-21  OSC开源社区    Tags:Redis   点击:(11)  评论:(0)  加入收藏
在Redis中如何实现分布式锁的防死锁机制?
在Redis中实现分布式锁是一个常见的需求,可以通过使用Redlock算法来防止死锁。Redlock算法是一种基于多个独立Redis实例的分布式锁实现方案,它通过协调多个Redis实例之间的锁...【详细内容】
2024-02-20  编程技术汇    Tags:Redis   点击:(49)  评论:(0)  加入收藏
手动撸一个 Redis 分布式锁
大家好呀,我是楼仔。今天第一天开工,收拾心情,又要开始好好学习,好好工作了。对于使用 Java 的小伙伴,其实我们完全不用手动撸一个分布式锁,直接使用 Redisson 就行。但是因为这些...【详细内容】
2024-02-19  楼仔  微信公众号  Tags:Redis   点击:(40)  评论:(0)  加入收藏
工作中Redis有哪些好用的运维工具
工作中使用 Redis 时,如果大家公司没有专业运维,可能开发人员就会面临这些运维的工作,包括 Redis 的运行状态监控,数据迁移,主从集群、切片集群的部署和运维等等。本文我就从这三...【详细内容】
2024-02-06  waynaqua    Tags:Redis   点击:(57)  评论:(0)  加入收藏
站内最新
站内热门
站内头条