目录
4.3、RabbitMQ高可用-镜像集群(解决普通集群的缺点)
面试题列表
- 为什么要用消息队列?(消息队列的应用场景?)
- 各种消息队列产品的比较?
- 消息队列的优点和缺点?
- 如何保证消息队列的高可用?
- 如何保证消息不丢失?
- 如何保证消息不被重复消费?(如何保证消息消费的幂等性?)
- 如何保证消息消费的顺序性?
- 基于MQ的分布式事务实现
1、为什么要用消息队列?(消息队列的应用场景?)
1.1、面试官心理分析
考察面试者是否知道消息队列用来解决什么样的问题?
也就是消息队列的应用场景
1.2、消息队列的本质
- 消息队列是一种“先进先出”的数据结构
- 常见应用场景:解耦、异步、削峰
1.3、解耦
系统的耦合性越高,容错性就越低。以电商应用为例,用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障或者因为升级等原因暂时不可用,都会造成下单操作异常,影响用户使用体验。
1.4、异步
A系统接收一个请求,需要在自己本地写库,还需要在B、C、D三个系统写库,自己本地写库要3ms,B、C、D三个系统分别写库要300ms、450ms、200ms。最终请求总延时是3+300+450+200=953ms,接近1s,用户非常不好,一般互联网类的企业,对于用户直接的操作,一般要求是每个请求都必须在200ms以内完成,对用户几乎是无感知的,如果用户通过浏览器发起请求,等待个1s,这几乎是不可接受的。
如果使用MQ,那么A系统连续发送3条消息到MQ队列中,假如耗时5ms,A系统从接受一个请求到返回响应给用户,总时长是3+5=8ms,对于用户而言,响应速度大大提升了,改善了用户的体验。
1.5、削峰/限流
应用系统如果遇到系统请求流量的瞬间猛增,有可能会将系统压垮。有了消息队列可以将大量请求缓存起来,分散到很长一段时间处理,这样可以大大提到系统的稳定性和用户体验。
1.5.1、流量削峰的经济考量
业务系统正常时段的QPS如果是2000,流量最高峰是10000,这里应该是应该冲着2000搭,还是10000搭服务器呢?
答案是2000,为了应对流量高峰配置高性能的服务器搭10000显然不划算,但是在高并发场景下,如秒杀场景QPS是大于2000的,系统肯定挂掉的,这时可以使用消息队列对峰值流量削峰限流为2000,从而保证系统不蹦。
一般情况,为了保证系统的稳定性,如果系统负载超过阙值,就会阻止用户请求,这会影响用户体验,而如果使用消息队列将请求缓存起来,等待系统处理完毕后通知用户下单完毕,这样总比不能下单体验要好。
1.6、小结
- 为什么要用消息队列
- 解耦
- 异步
- 削峰
2、各种消息队列产品的比较
2.1、面试官心理分析
考察面试者是否对市面上的MQ产品做过调研?
在选择MQ时是否根据不同MQ的产品特点做过对比和取舍?
2.2、几种MQ的对比
2.3、小结
- ActiveMQ ,早起使用的较多,没经过大规模吞吐量场景的验证,社区也不是很活跃,但是现在确实大家用的不多了,不推荐。
- RabbitMQ ,开发语言 erlang 阻止了大量的 Java。工程师去深入研究和掌控它,对公司而言,几乎处于不可控的状态,但是 RabbitMQ 是开源的,比较德定的支持,活跃度也高,如不考虑二次开发,追求性能和稳定性,推荐使用。
- RocketMQ ,开发语言是 Java ,在阿里内部经受过高并发业务的考验,稳定性和性能均不错,考虑后期可能二次开发,推荐使用。
- Kafka,大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,社区活跃度很高,推荐使用。大数据领域日志采集等业务推荐使用。
- ZeroMQ,底层是C/C++开发的,但是目前支持了多种语言对它的封装,可以说是夸平台、跨语言的。另外ZeroMQ是嵌入到程序内部的(类似程序中使用第三方api),而其他MQ是独立的一个服务。
3、消息队列的优点和缺点
3.1、面试官心理分析
考察面试者在项目中引入一个新的技术后,是否全面考虑过新的技术对当前项目的影响。
3.2、系统可用性降低
系统引入的外部依赖越多,系统稳定性越差。一旦MQ宕机,就会对业务造成影响。---如何保证MQ的高可用?
3.3、系统复杂度提高
MQ的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过MQ进行异步调用。
引入问题有:
- 消息丢失怎么办?
- 重复消息怎么处理?
- 如何保证消息传递的顺序性?
3.4、一致性问题
A系统处理完业务,通过MQ给B、C、D三个系统发消息数据,如果B系统、C系统处理成功,D系统处理失败。
如何保证消息数据处理的一致性?
3.5、小结
- 消息队列的优点和缺点?
优点
- 解耦
- 异步
- 削峰
缺点
- 系统可用性降低
- 系统复杂度提高
- 一致性问题
4、如何保证消息队列的高可用?
4.1、面试官心理分析
项目中引入了MQ导致系统的可用性降低,面试官想知道的是面试者针对可用性降低问题的思考和解决思路。
问答此问题时,面试者只需针对自己使用过的一两个MQ产品的高可用方案回答即可。
4.2、RabbitMQ高可用-普通集群
- 在多台机器上分别启动RabbitMQ实例。
- 多个实例之间可以相互通信。
- 创建的Queue只会放在一个RabbitMQ上,其他实例都同步元数据。
- 消费的时候,如果连接的没有Queue,那么当前实例会从queue所在实例拉取数据。
特点
1.没有真正做到高可用(左右节点从中间节点拉取业务数据然后在给消费者消费,当左右节点宕机不影响,当中间节点宕机了就真的宕机了,业务数据将不能获取了)。
2.有数据拉取的开销和单实例的瓶颈问题(所有数据要到中间节点拉取,这样会有性能的瓶颈)。
图中元数据是配置数据,实际数据是业务数据。
4.3、RabbitMQ高可用-镜像集群(解决普通集群的缺点)
- 在多台机器上分别启动 RabbitMO实例。
- 多个实例之间可以相互通信。
- 每次生产者写消息到 queue的时候,都会自动把消息同步到多个实例的 queue上。每个RabbitMQ节点上都有 Queue的消息数据和元数据。
- 某一个节点宕机,其他节点依然保存完整数据,不影响客户端消费。
4.4、RocketMQ高可用-双主双从
- 生产者通过Name Server发现Broker。
- 生产者发送队列消息到2个Broker主节点。
- Broker主节点分别和各自从节点同步数据。
- 消费者从主或者从节点订阅消息。
图中Name Server可以理解为管理者,Producer发消息前会询问Name Server发送到哪个主节点,主节点收到数据后会同步到从节点,主从节点和Name Server通过心跳机制的方式可以知道节点是否正常运行,消费者可以从主节点、从节点消费数据
4.5、小结
- MQ的高可用如何保证?
- RabbitMQ::镜像集群
- RocketMQ:双主双从
5、如何保证消息不丢失?
5.1、面试官心理分析
- 考察面试者是否清楚消息丢失的原因?
- 如何保证消息不丢失?
5.2、消息丢失的原因
情况一:消息生产者没有成功发送到MQ Broker。
情况二:消息发送给MQ Broker后,Broker宕机导致内存中的消息数据丢失。
情况三:消费者获取到消息,但消费者还没有来得及处理宕机了,但此时MQ中消息已经删除,消费者重启后不能再消费之前的消息。
5.3、确保消息不丢失方案
- 消息发送者发送给 MQ Broker后, MQ Broker给生产者确认收到。
- MQ收到消息后进行消息持久化。
- 消费者收到消息处理完毕后手动进行ack确认。
- MQ收到消费者ack确认后删除持久化的消息。
5.4、小结
消息丢失的原因
- 发送方、MQ、消费方都有可能导致消息丢失。
如何保证消息不丢失
- 发送方可靠发送。
- MQ进行消息持久化。
- 消费方消费完毕进行ACK确认,MQ收到消费方的ACK确认再删除本地消息。
6、如何保证消息不被重复消费?
6.1、面试官心理分析
消息的重复消费和保证幂等性会经常被连着问,当出现了重复消费后,为了保证系统数据的正常性,就必须要保证幂等性。针对该问题,面试官实际想要考察的是:
- 面试者对重复消费产生原因的思考
- 面试者对保证消息幂等性的方案
6.2、重复消息产生的原因
消息重复的根本原因是网络不可达。
- 发送时消息重复
当一条消息已被成功发送到服务端,此时出现了网络闪断,导致服务端对客户端应答失败。如果此时生产者意识到消息发送失败并尝试再次发送消息,消费者后续会收到两条内容相同的消息。
6.3、消息幂等性
- 消息发送者发送消息时携带一个全局唯一的消息id。
- 消费者获取消费后先根据id在redis/db中查询是否存在消费记录。
- 如果没有消费过就正常消费,消费完毕后写入redis/db。
- 如果消息消费过就直接舍弃。
6.4、小结
- MQ消息的重复问题和幂等性保证?
消息重复原因
- 网络不可达,不可避免。
幂等性
- 消息携带全局ID,消费方接到消息时先查在处理,根据全局ID做判重操作。
7、如何保证消息消费的顺序性?
7.1、面试官心理分析
- 考察消费者是否理解什么是消息顺序消费?
- 考察消费者是否思考过确保消息顺序消费的方案?
7.2、消息的顺序性消费
消息有序指的是可以按照消息的发送顺序来消费。
例如:一笔订单产生了3条消息,分别是订单创建、订单付款、订单完成。
消费时,要按照顺序依次消费才有意义。
与此同时多笔订单之间又是可以并行消费的。
假设消费者顺序有张三:M1、M2、M3,李四S1、S2、S3。
- 模式一:M1、M2按照先后顺序发送到2台Server,被2个消费者分别消费(PASS)。
- 不能保证消息的顺序到达MQ。
- 不能保证消息的顺序消费。
- 模式二:M1、M2按照先后顺序发送到1台Server,被2个消费者分别消费(PASS)。
- 可以保证消息的顺序到达MQ。
- 消费方如果出现网络延迟问题,不能严格保证消息的顺序消费。
- 模式三:生产者:MQ Server:消费者=1:1:1(全局顺序性)
- 可以保证消息顺序到达MQ。
- 可以保证消息顺序消费。
全局的顺序消费(即张三的消息没有消费完的话,李四的消息是不能消费的),不常见!
- 吞吐量下降。
- 容错性降低。
该模式在M1被消费并且应答后,再消费M2,如此保证顺序性。
- 模式四:局部顺序消费
- 生产者根据消息ID将同一组消息发送到一个Queue中。
- 多个消费者同时获取Queue中的消息进行消费。
- MQ使用分段锁保证单个Queue中的有序消费。
该模式既保证了并行消费,也保证了顺序消费。
当消费者1 从Queue1 取了M1时,给Queue1加分段锁,当消费者1 ack后再解锁,让消费者2从Queue1中取M2再加锁,以此完成局部顺序性。
这里张三的消息只能到同一个消息队列,比如M1、M2、M3都在Queue1,S1、S2、S3都在Queue3.
7.3、小结
什么是消息的顺序型消费?
- 消费方按照消息发送的顺序进行消费,分为全局顺序消息和局部顺序消息。
- 常见的局部顺序消费
如何保证消息的顺序消费?
- 全局顺序消息,生产者:MQ:消费者=1:1:1
- 局部顺序消息:
- 生产者将同一组消息发送到单个队列。
- 多个消费者并行对消息进行消费。
- Queue通过分段锁保证消息消费的顺序性。
8、基于MQ的分布式事务实现
8.1、分布式事务
- 用户提交订单。
- 库存服务操作库存DB,减库存。
- 订单服务操作订单DB,生成订单数据。
- 库存服务和订单服务要么同时成功,要么同时失败。
本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
处理流程:
- 消息发送方先将消息存储到本地消息表并置消息状态type=1表示未处理,然后发送消息到MQ,消息消费方监听到消息后,根据id查询本地消息表是否重复,如果未重复则继续执行并存储本地消息表,再发送成功到MQ。消息发送方监听到消费方处理成功后,将本地消息表的消息状态type置为2表示已处理。
- 当消费方处理失败的时候,此时消费方没有正常发送消息到MQ,因为消息发送方有个定时任务,定时查询本地消息表,将type=1的数据重新发送到MQ,接着触发消费方消费。
8.2、小结
消息发送方:
- 处理业务逻辑。
- 保存消息到本地数据库。
- 发送消息给MQ。
- 监听MQ消息方通知消息,更改消息状态为已处理。
- 定时任务将长期未处理消息重新发送到MQ。
消息消费方:
- 监听MQ中间件消息。
- 判断消息是否重复,重复就丢弃。
- 消息未重复,执行本地业务。
- 业务处理完毕,写消息记录到本地数据库。
- 发送通知消息到MQ。