好!现在大家对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过小导致吞吐量过低
这些底层机制和问题,咱们都一步步分析清楚了 。
所以到现在,单论消费端这块的数据不丢失技术方案,相信大家在面试的时候就可以有一整套自己的理解和方案可以阐述了 。
推荐阅读
- 原理及代码实现 Redis分布式锁最全详解
- 空气培养皿的放置方法及时间
- 番茄酱炒年糕的做法窍门 番茄酱炒年糕的做法
- 周易|周易卦例:真是顺利,三套房就选中了财运旺的办公室
- 魏无羡|你们把角色留在了19年,而我却选择陪着角色留下来了
- 李湘|王岳伦陪读父爱满满,李湘去阿联酋引猜测,两人复合仍是好选择
- 张宁益|马琳前妻:定居美国当全职太太,看来二婚选对人
- 除尘布袋是用什么材质做成的 除尘布袋材质及介绍
- 家庭化粪池的做法 家庭化粪池结构图
- 加康加年味|秦子越谈聂远:这么帅肯定没安全!为爱做家庭主妇!深受婆婆稀罕