Java面试题冲刺第十六天--消息队列


Posted in 面试题 onAugust 07, 2021

面试题1:说说你对消息队列的理解,消息队列为了解决什么问题?

我们公司业务系统一开始体量较小,很多组件都是单机版就足够,后来随着用户量逐渐扩大,我们程序也采用了微服务的设计思想,把很多服务进行了拆分,但后来在一些秒杀抢票活动或高频业务中,服务依旧扛不住大量QPS,因此我们引入了消息队列来优化该类问题。

消息队列应用的场景大致分为三类:解耦异步削峰

解耦

消息队列类似设计模式中的观察者模式(Observer)发布-订阅模式(Pub-Sub)。生产者生成和发送消息到消息队列,消费者从消息队列中取走消息进行处理,称为消费,使用消息队列将“生产者”和“消费者”之间的操作关联解耦,易于扩展。

Java面试题冲刺第十六天--消息队列

比如系统A为支付系统,一开始用户支付完调用日志记录系统B记录就完了,后来内容越来越多,支付完成要调用加积分系统C、短信通知系统D、优惠券系统E等等…

Java面试题冲刺第十六天--消息队列

这个场景中,A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条支付成功的数据,很多系统接口都需要 A 系统调用把支付成功的数据发送过去。A 系统程序员要时刻考虑这些问题:

  • 其他系统如果挂了该咋办?是不是直接程序抛异常了?
  • 一天到晚加业务,每次都重新部署?领导是不是狗?

那如果引入 MQ,A 系统产生一条数据,发送到 MQ 里面去,每个子系统加上对消息队列中支付成功消息的订阅,持续监听就可以了,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从 MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。

Java面试题冲刺第十六天--消息队列

这样下来,A系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况,我只负责把支付成功的信息放到MQ里就行了,至于能否正常加积分、能否正常短信通知,管我鸟事!~~可见,通过一个 MQ,Pub/Sub 发布订阅消息这么一个模型,A 系统就跟其它系统彻底解耦了。

面试官:哦,那我听出来了,你这是喜欢甩锅啊!来,简历还你。

我:额。。不,我开玩笑的,当然不能这样做,这里其实涉及到MQ在分布式事务中数据一致性的问题;听我跟您解释。

数据一致性

这个其实是分布式服务本身就存在的一个问题,不仅仅是消息队列的问题,但是放在这里说是因为用了消息队列这个问题会更明显。

就像咱们上面说的,你支付成功的服务自己保证自己的逻辑成功处理了,你成功发了消息,但是短信系统,积分系统等等这么多系统,他们成功还是失败你就不管了?当然不行,这样坑队友的行为,狄大人都帮不了你~

怎么办?那就把所有的服务都放到一个事务里,所有都成功成功才能算这一次下单是成功的,要成功一起成功,要失败一起失败。

异步

A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、400ms、200ms。最终请求总延时是 3 + 300 + 400 + 200 = 903ms,接近 1秒,用户感觉搞个毛线?慢的一批。

Java面试题冲刺第十六天--消息队列

一般互联网类的企业,对于用户直接的操作,一般要求是每个请求都必须在 200 ms 以内完成,对用户几乎是无感知的,如果1秒足以说明该系统不可用,垃圾系统。

如果这里使用了消息队列,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms,对于用户而言,其实感觉上就是点个按钮,8ms 以后就直接返回了,体验感很好

削峰

比如我们系统有代售抢票业务,平时每天QPS也就50左右,A 系统风平浪静。结果每次一到春运抢票,每秒并发请求数量突然会暴增到10000以上。但是系统是直接基于 MySQL 的,大量的请求直接打到 MySQL,比如一般MySQL能抗2000条请求,现在每秒10000 条 SQL,可能就直接把 MySQL 给打死了,导致系统崩溃。但是高峰期一过就又没人了,QPS回到50,对整个系统几乎没有任何的压力。

Java面试题冲刺第十六天--消息队列

如果这里使用 MQ,每秒 1w 个请求写入 MQ,A 系统每秒钟最多处理 2000 个请求,因为 MySQL 每秒钟最多处理 2k 个。A 系统从 MQ 中慢慢拉取请求,每秒钟就拉取 2k 个请求,不要超过自己每秒能处理的最大请求数量就 ok了,这样下来,哪怕是高峰期的时候,A 系统也不会挂掉。当然了,用户的响应时间肯定会受影响,毕竟秒杀嘛,只要把前多少条请求处理好,其余的抢票失败就行了。

另外,MQ 每秒钟 1w 个请求进来,只处理 2k 个请求出去,结果会导致在中午高峰期,可能有几十万甚至几百万的请求积压在 MQ 中。

这个短暂的高峰期积压是 ok 的,因为高峰期过了之后,每秒钟就 50 个请求进 MQ,但是A 系统依然会按照每秒 2k 个请求的速度在处理。所以说,只要高峰期一过,A 系统就会快速将积压的消息给消费掉。

追问1:消息队列有什么优缺点

  • 系统可用性降低

系统引入的外部依赖越多,越容易挂掉。本来你就是 A 系统调用 BCD 三个系统的接口就好了,人 ABCD 四个系统好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了咋整,MQ 一挂,整套系统崩溃的,你不就完了?如何保证消息队列的高可用?

  • 系统复杂度提高

硬生生加个 MQ 进来,你怎么保证消息一定被消费?如何避免消息重复投递或重复消费?数据丢失怎么办?怎么保证消息传递的顺序性?

  • 一致性问题

A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,咋整?你这数据就不一致了。

面试题2:对于消息中间机,你们是怎么做技术选型的?

目前市面上比较主流的消息队列中间件主要有,Kafka、ActiveMQ、RabbitMQ、RocketMQ 等。

ActiveMQ和RabbitMQ这两由于吞吐量的原因,只有业务体量一般的公司在用,RabbitMQ由于是erlang语言开发的,我们都不了解,因此扩展和维护成本都很高,查个问题都头疼。

Kafka和RocketMQ一直在各自擅长的领域发光发亮,两者的吞吐量、可靠性、时效性等都很可观。

我们通过图表看看这几个消息中间机的对比:

Java面试题冲刺第十六天--消息队列

大家其实一下子就能看到差距了,就拿吞吐量来说,早期比较活跃的ActiveMQ 和RabbitMQ基本上不是后两者的对手了,在现在这样大数据的年代吞吐量是真的很重要。

面试题3:如何确保消息正确地发送至 RabbitMQ?如何确保消息接收方消费了消息?

发送方确认模式

将信道设置成confirm模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的ID。

一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一ID)。

如果RabbitMQ发生内部错误从而导致消息丢失,会发送一条Nack(not acknowledged,未确认)消息。

发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。

接收方确认机制

消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ才能安全地把消息从队列中删除。

这里并没有用到超时机制,RabbitMQ仅通过Consumer的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ给了Consumer足够长的时间来处理消息。保证数据的最终一致性;

追问1:如何保证MQ消息的可靠传输?

以我们常用的RabbitMQ为例,消息不可靠的情况可能是消息丢失,劫持等原因;

丢失又分为:生产者丢失消息、消息队列丢失消息、消费者丢失消息;

生产者丢失消息:从生产者弄丢数据这个角度来看,RabbitMQ提供confirm模式来确保生产者不丢消息;

confirm模式用的居多:一旦channel进入confirm模式,所有在该信道上发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后;RabbitMQ就会发送一个ACK给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了;

如果rabbitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。

消息队列丢数据:消息持久化。

处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。

持久化配置和confirm机制配合使用,在消息持久化磁盘后,再给生产者发送一个Ack信号。

这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您关注三水点靠木的更多内容!


Tags in this post...

面试题 相关文章推荐
在SQL Server中创建数据库主要有那种方式
Sep 10 面试题
几道数据库的面试题或笔试题
May 31 面试题
广州迈达威.net面试题目
Mar 10 面试题
.net工程师笔试题
Jun 09 面试题
linux面试题参考答案(10)
Oct 26 面试题
几个Linux面试题笔试题
Dec 01 面试题
某公司部分笔试题
Nov 05 面试题
什么是设计模式
Jun 17 面试题
你经历的项目中的SCM配置项主要有哪些?什么是配置项?
Nov 04 面试题
AJax面试题
Nov 25 面试题
什么是GWT的Entry Point
Aug 16 面试题
用Python写一个for循环的例子
Jul 19 面试题
Java面试题冲刺第十五天--设计模式
Promise面试题详解之控制并发
北京捷通华声语音技术有限公司Java软件工程师笔试题
Apr 10 #面试题
顺丰快递Java软件工程师面试题
Jul 31 #面试题
Java软件工程师综合面试题笔试题
Sep 08 #面试题
JAVA软件工程师测试题
Jul 25 #面试题
请介绍一下WSDL的文档结构
Mar 17 #面试题
You might like
用PHP制作静态网站的模板框架(二)
2006/10/09 PHP
PHP 和 XML: 使用expat函数(一)
2006/10/09 PHP
PHP 修复未正常关闭的HTML标签实现代码(支持嵌套和就近闭合)
2012/06/07 PHP
javascript椭圆旋转相册实现代码
2012/01/16 Javascript
jQuery客户端分页实例代码
2013/11/18 Javascript
javascript实现base64 md5 sha1 密码加密
2015/09/09 Javascript
jQuery基于json与cookie实现购物车的方法
2016/04/15 Javascript
浅谈JavaScript中小数和大整数的精度丢失
2016/05/31 Javascript
JS实现定时任务每隔N秒请求后台setInterval定时和ajax请求问题
2017/10/15 Javascript
通过vue-router懒加载解决首次加载时资源过多导致的速度缓慢问题
2018/04/08 Javascript
浅谈在node.js进入文件目录的问题
2018/05/13 Javascript
详解javascript中的babel到底是什么
2018/06/21 Javascript
ES7之Async/await的使用详解
2019/03/28 Javascript
详解vue-cli项目在IE浏览器打开报错解决方法
2020/12/10 Vue.js
让python在hadoop上跑起来
2016/01/27 Python
在Python程序和Flask框架中使用SQLAlchemy的教程
2016/06/06 Python
Python实现控制台中的进度条功能代码
2017/12/22 Python
python实现mysql的读写分离及负载均衡
2018/02/04 Python
Python面向对象之类和对象属性的增删改查操作示例
2018/12/14 Python
在cmd中查看python的安装路径方法
2019/07/03 Python
python使用paramiko模块通过ssh2协议对交换机进行配置的方法
2019/07/25 Python
Django stark组件使用及原理详解
2019/08/22 Python
Python 调用 Windows API COM 新法
2019/08/22 Python
Python Print实现在输出中插入变量的例子
2019/12/25 Python
Win下PyInstaller 安装和使用教程
2019/12/25 Python
分享全球十款超强HTML5开发工具
2014/05/14 HTML / CSS
产品工艺师的岗位职责
2013/11/15 职场文书
2014年客服工作总结范文
2014/11/13 职场文书
保送生自荐信
2015/03/06 职场文书
学习与创新自我评价
2015/03/09 职场文书
入党积极分子半年考察意见
2015/06/02 职场文书
高温慰问简报
2015/07/21 职场文书
电工生产实习心得体会
2016/01/22 职场文书
pycharm debug 断点调试心得分享
2021/04/16 Python
Redis持久化与主从复制的实践
2021/04/27 Redis
Kubernetes中Deployment的升级与回滚
2022/04/01 Servers