作者浩说编程 来源公众号:浩说编程 〔大厂技术资源研发必备安装包经典必读电子书限时免费获取〕 问题导读 一、说说你对消息队列的理解,消息队列为了解决什么问题? 二、消息队列有什么优缺点? 三、说一下RabbitMQ有哪些工作模式? 一、说说你对消息队列的理解,消息队列为了解决什么问题?正经回答: 我们公司业务系统一开始体量较小,很多组件都是单机版就足够,后来随着用户量逐渐扩大,我们程序也采用了微服务的设计思想。 把很多服务进行了拆分,但后来在一些秒杀抢票活动或高频业务中,服务依旧扛不住大量QPS,因此我们引入了消息队列来优化该类问题。 消息队列应用的场景大致分为三类:解耦、异步、削峰。 解耦 消息队列类似设计模式中的观察者模式(Observer)或发布订阅模式(PubSub)。生产者生成和发送消息到消息队列,消费者从消息队列中取走消息进行处理,称为消费,使用消息队列将生产者和消费者之间的操作关联解耦,易于扩展。 比如系统A为支付系统,一开始用户支付完调用日志记录系统B记录就完了,后来内容越来越多,支付完成要调用加积分系统C、短信通知系统D、优惠券系统E等等 这个场景中,A系统跟其它各种乱七八糟的系统严重耦合,A系统产生一条支付成功的数据,很多系统接口都需要A系统调用把支付成功的数据发送过去。A系统程序员要时刻考虑这些问题:其他系统如果挂了该咋办?是不是直接程序抛异常了?一天到晚加业务,每次都重新部署?领导是不是狗? 那如果引入MQ,A系统产生一条数据,发送到MQ里面去,每个子系统加上对消息队列中支付成功消息的订阅,持续监听就可以了,哪个系统需要数据自己去MQ里面消费。如果新系统需要数据,直接从MQ里消费即可;如果某个系统不需要这条数据了,就取消对MQ消息的消费即可。 这样下来,A系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况,我只负责把支付成功的信息放到MQ里就行了,至于能否正常加积分、能否正常短信通知,管我鸟事!可见,通过一个MQ,PubSub发布订阅消息这么一个模型,A系统就跟其它系统彻底解耦了。 面试官:哦,那我听出来了,你这是喜欢甩锅啊!来,简历还你。 我:额不,我开玩笑的,当然不能这样做,这里其实涉及到MQ在分布式事务中数据一致性的问题;听我跟您解释。 数据一致性 这个其实是分布式服务本身就存在的一个问题,不仅仅是消息队列的问题,但是放在这里说是因为用了消息队列这个问题会更明显。 就像咱们上面说的,你支付成功的服务自己保证自己的逻辑成功处理了,你成功发了消息,但是短信系统,积分系统等等这么多系统,他们成功还是失败你就不管了?当然不行,这样坑队友的行为,狄大人都帮不了你 怎么办?那就把所有的服务都放到一个事务里,所有都成功成功才能算这一次下单是成功的,要成功一起成功,要失败一起失败。 异步 A系统接收一个请求,需要在自己本地写库,还需要在BCD三个系统写库,自己本地写库要3ms,BCD三个系统分别写库要300ms、400ms、200ms。最终请求总延时是3300400200903ms,接近1秒,用户感觉搞个毛线?慢的一批。 一般互联网类的企业,对于用户直接的操作,一般要求是每个请求都必须在200ms以内完成,对用户几乎是无感知的,如果1秒足以说明该系统不可用,垃圾系统。 如果这里使用了消息队列,那么A系统连续发送3条消息到MQ队列中,假如耗时5ms,A系统从接受一个请求到返回响应给用户,总时长是358ms,对于用户而言,其实感觉上就是点个按钮,8ms以后就直接返回了,体验感很好 削峰 比如我们系统有代售抢票业务,平时每天QPS也就50左右,A系统风平浪静。结果每次一到春运抢票,每秒并发请求数量突然会暴增到10000以上。但是系统是直接基于MySQL的,大量的请求直接打到MySQL,比如一般MySQL能抗2000条请求,现在每秒10000条SQL,可能就直接把MySQL给打死了,导致系统崩溃。但是高峰期一过就又没人了,QPS回到50,对整个系统几乎没有任何的压力。 如果这里使用MQ,每秒1w个请求写入MQ,A系统每秒钟最多处理2000个请求,因为MySQL每秒钟最多处理2k个。A系统从MQ中慢慢拉取请求,每秒钟就拉取2k个请求,不要超过自己每秒能处理的最大请求数量就ok了,这样下来,哪怕是高峰期的时候,A系统也不会挂掉。当然了,用户的响应时间肯定会受影响,毕竟秒杀嘛,只要把前多少条请求处理好,其余的抢票失败就行了。 另外,MQ每秒钟1w个请求进来,只处理2k个请求出去,结果会导致在中午高峰期,可能有几十万甚至几百万的请求积压在MQ中。 这个短暂的高峰期积压是ok的,因为高峰期过了之后,每秒钟就50个请求进MQ,但是A系统依然会按照每秒2k个请求的速度在处理。所以说,只要高峰期一过,A系统就会快速将积压的消息给消费掉。 二、消息队列有什么优缺点?正经回答: 优点上面已经说了,就是在特殊场景下有其对应的好处,解耦、异步、削峰。 缺点有以下几个: 系统可用性降低 本来系统运行好好的,现在你非要加入个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性会降低; 系统复杂度提高 加入了消息队列,要多考虑很多方面的问题,比如:一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等。 因此,需要考虑的东西更多,复杂性增大。 一致性问题 A系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是BCD三个系统那里,BD两个系统写库成功了,结果C系统写库失败了,咋整?你这数据就不一致了。 所以消息队列实际是一种非常复杂的架构,你引入它有很多好处,但是也得针对它带来的坏处做各种额外的技术方案和架构来规避掉,做好之后,你会发现,妈呀,系统复杂度提升了一个数量级,也许是复杂了10倍。但是关键时刻,用,还是得用的。 三、说一下RabbitMQ有哪些工作模式?正经回答: 一。simple模式(即最简单的收发模式) 1。消息产生消息,将消息放入队列 2。消息的消费者(consumer)监听消息队列,如果队列中有消息,就消费掉,消息被拿走后,自动从队列中删除(隐患消息可能没有被消费者正确处理,已经从队列中消失了,造成消息的丢失,这里可以设置成手动的ack,但如果设置成手动ack,处理完后要及时发送ack消息给队列,否则会造成内存溢出)。 二。work工作模式(资源的竞争) 1。消息产生者将消息放入队列消费者可以有多个,消费者1,消费者2同时监听同一个队列,消息被消费。C1C2共同争抢当前的消息队列内容,谁先拿到谁负责消费消息(隐患:高并发情况下,默认会产生某一个消息被多个消费者共同使用,可以设置一个开关(syncronize)保证一条消息只能被一个消费者使用)。 三。publishsubscribe发布订阅(共享资源) 1、每个消费者监听自己的队列; 2、生产者将消息发给broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将接收到消息。 四。routing路由模式 1。消息生产者将消息发送给交换机按照路由判断,路由是字符串(info)当前产生的消息携带路由字符(对象的方法),交换机根据路由的key,只能匹配上路由key对应的消息队列,对应的消费者才能消费消息; 2。根据业务功能定义路由字符串 3。从系统的代码逻辑中获取对应的功能字符串,将消息任务扔到对应的队列中。 4。业务场景:error通知;EXCEPTION;错误通知的功能;传统意义的错误通知;客户通知;利用key路由,可以将程序中的错误封装成消息传入到消息队列中,开发者可以自定义消费者,实时接收错误; 五。topic主题模式(路由模式的一种) 1。星号井号代表通配符 2。星号代表多个单词,井号代表一个单词 3。路由功能添加模糊匹配 4。消息产生者产生消息,把消息交给交换机 5。交换机根据key的规则模糊匹配到对应的队列,由队列的监听消费者接收消息消费 (在我的理解看来就是routing查询的一种模糊匹配,就类似sql的模糊查询方式) 每日小结今天我们复习了面试中常考的消息队列三个问题,你做到心中有数了么?1 对了,如果你的朋友也在准备面试,请将这个系列扔给他,如果他认真对待,肯定会感谢你的!!好了,今天就到这里,学废了的同学,记得三连,也会给我继续更新的动力。1 另外,我在同名公众号中为大家准备了〔大厂技术资源研发必备安装包经典必读电子书限时免费获取〕 欢迎来撩!