面试大杀器:消息中间件如何实现消费吞吐量的百倍优化?【石杉的架构笔记】

欢迎关注我的公众号:石杉的架构笔记(ID:shishan100)面试

周一至周五早8点半!精品技术文章准时送上!算法

目录

(1)前请提示性能优化

(2)unack消息的积压问题架构

(3)如何解决unack消息的积压问题并发

(4)高并发场景下的内存溢出问题异步

(5)低吞吐量问题分布式

(6)合理设置prefetch count微服务

(7)阶段性总结高并发

一、前情提示

上一篇文章:互联网面试必杀:如何保证消息中间件全链路数据100%不丢失(2),咱们分析了ack机制的底层实现原理(delivery tag机制),还有消除处理失败时的nack机制如何触发消息重发。oop

经过这个,已经让你们进一步对消费端保证数据不丢失的方案的理解更进一层了。

这篇文章,咱们将会对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太小致使吞吐量太低

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

因此到如今,单论消费端这块的数据不丢失技术方案,相信你们在面试的时候就能够有一整套本身的理解和方案能够阐述了。

接下来下篇文章开始,咱们就来具体聊一聊:消息中间件的生产端如何保证数据不丢失。

end

若有收获,请帮忙转发,您的鼓励是做者最大的动力,谢谢!

一大波微服务、分布式、高并发、高可用的原创系列文章正在路上

欢迎扫描下方二维码,持续关注:

石杉的架构笔记(id:shishan100)

十余年BAT架构经验倾囊相授

推荐阅读:

一、拜托!面试请不要再问我Spring Cloud底层原理

二、【双11狂欢的背后】微服务注册中心如何承载大型系统的千万级访问?

三、【性能优化之道】每秒上万并发下的Spring Cloud参数优化实战

四、微服务架构如何保障双11狂欢下的99.99%高可用

五、兄弟,用大白话告诉你小白都能听懂的Hadoop架构原理

六、大规模集群下Hadoop NameNode如何承载每秒上千次的高并发访问

七、【性能优化的秘密】Hadoop如何将TB级大文件的上传性能优化上百倍

八、拜托,面试请不要再问我TCC分布式事务的实现原理!

九、【坑爹呀!】最终一致性分布式事务如何保障实际生产中99.99%高可用?

十、拜托,面试请不要再问我Redis分布式锁的实现原理!

十一、【眼前一亮!】看Hadoop底层算法如何优雅的将大规模集群性能提高10倍以上?

十二、亿级流量系统架构之如何支撑百亿级数据的存储与计算

1三、亿级流量系统架构之如何设计高容错分布式计算系统

1四、亿级流量系统架构之如何设计承载百亿流量的高性能架构

1五、亿级流量系统架构之如何设计每秒十万查询的高并发架构

1六、亿级流量系统架构之如何设计全链路99.99%高可用架构

1七、七张图完全讲清楚ZooKeeper分布式锁的实现原理

1八、大白话聊聊Java并发面试问题之volatile究竟是什么?

1九、大白话聊聊Java并发面试问题之Java 8如何优化CAS性能?

20、大白话聊聊Java并发面试问题之谈谈你对AQS的理解?

2一、大白话聊聊Java并发面试问题之公平锁与非公平锁是啥?

2二、大白话聊聊Java并发面试问题之微服务注册中心的读写锁优化

2三、互联网公司的面试官是如何360°无死角考察候选人的?(上篇)

2四、互联网公司面试官是如何360°无死角考察候选人的?(下篇)

2五、Java进阶面试系列之一:哥们,大家的系统架构中为何要引入消息中间件?

2六、【Java进阶面试系列之二】:哥们,那你说说系统架构引入消息中间件有什么缺点?

2七、【行走的Offer收割机】记一位朋友斩获BAT技术专家Offer的面试经历

2八、【Java进阶面试系列之三】哥们,消息中间件在大家项目里是如何落地的?

2九、【Java进阶面试系列之四】扎心!线上服务宕机时,如何保证数据100%不丢失?

30、一次JVM FullGC的背后,竟隐藏着惊心动魄的线上生产事故!

3一、【高并发优化实践】10倍请求压力来袭,你的系统会被击垮吗?

3二、【Java进阶面试系列之五】消息中间件集群崩溃,如何保证百万生产数据不丢失?

3三、亿级流量系统架构之如何在上万并发场景下设计可扩展架构(上)?

3四、亿级流量系统架构之如何在上万并发场景下设计可扩展架构(中)?

3五、亿级流量系统架构之如何在上万并发场景下设计可扩展架构(下)?

3六、亿级流量架构第二弹:你的系统真的无懈可击吗?

3七、亿级流量系统架构之如何保证百亿流量下的数据一致性(上)

3八、亿级流量系统架构之如何保证百亿流量下的数据一致性(中)?

3九、亿级流量系统架构之如何保证百亿流量下的数据一致性(下)?

40、互联网面试必杀:如何保证消息中间件全链路数据100%不丢失(1)

4一、互联网面试必杀:如何保证消息中间件全链路数据100%不丢失(2

做者:石杉的架构笔记 连接:juejin.im/post/5c263a… 来源:掘金 著做权归做者全部,转载请联系做者得到受权!

相关文章
相关标签/搜索