MQ面试问题整理
项目中为什么使用MQ
优点
- 解耦
同一块业务的相关能力被很多个项目需要,后续也有极大可能会被其他项目需要。将该部分能力解耦,通过MQ进行消息的统一处理。若有其他项目或业务需要相关功能,则对此进行订阅,处理相关业务。减少项目直接依赖。 - 异步
大型项目相关协作人员,参与团队越来越多,项目之间的关联越来越深。造成整个链路特别长,且问题排查不容易。 - 削峰
对于部分业务,在某些时间点,可能因为瞬时请求过大,造成所有请求直接访问至数据库对数据库造成压力,从而影响项目原有业务。一般MySQL数据库的处理能力在2k次/s,MQ的高峰处理能力1w+。能有效的处理
缺点
系统可用性降低
由于新增加了MQ,因为MQ的可用性、可靠性会造成系统可用性的额外负担。
- 可用性问题如何解决
可用性一般侧重于数据的持久化,防丢失。不同的MQ对此的解决方案不一样。
ActiveMQZookeeper
及多个activeMQ
,修改持久化为性能更好的LevelDB
替换掉默认的KahaDB
.
优点:简单实现了可用性。
缺点:每次实际可用的MQ只有一个,一旦有一个MQ出现问题,相关的消息就会丢失。
RocketMQ
- 单master模式,一旦出现问题,就会整个服务不可用。一般不会直接生产使用,适合个人学习;
- 多master模式,多个master节点组成集群。单个master出现问题不影响。优点:所有模式中性能最高的。 缺点:单个master节点宕机期间,未被消费的消息在节点恢复之前不可用,消息实时性就受到影响。注意:使用同步刷盘可保证消息不丢失,同时topic相对应的queue应该分布在集权中各个master节点,而不是只在某一个master节点上;
- 多master多slave异步复制模式,在多master模式下,每个master节点至少都有一个对应的slave;
master节点可读可写,slave只可读 不可写。优点:在master宕机时,消费者可以直接从slave读取消息,消息实时性不会受到影响,性能几乎与多master一样。缺点:使用异步复制可能造成消息丢失问题。 - 多master多slave同步双写,同上一种模式类似,区别在于master与slave之间的数据同步方式。优点:同步双写的同步模式能保证数据不丢失。缺点:发送单个消息时长会比较长,性能会比较差;
同步刷盘,异步刷盘的区别:在于多master之间的数据同步写入磁盘模式。
同步复制 | 异步复制 | |
---|---|---|
概念 | 即等 Master 和 Slave 均写成功后才反馈给客户端写成功状态 | 只要 Master 写成功,就反馈客户端写成功状态 |
可靠性 | 可靠性高,若 Master 出现故障,Slave 上有全部的备份数据,容易恢复 | 若 Master 出现故障,可能存在一些数据还没来得及写入 Slave,可能会丢失 |
效率 | 由于是同步复制,会增加数据写入延迟,降低系统吞吐量 | 由于只要写入 Master 即可,故数据写入延迟较低,吞吐量较高 |
实际应用中的推荐把Master和Slave设置成ASYNC_FLUSH的异步刷盘方式,主从之间配置成SYNC_MASTER的同步复制方式,这样即使有一台机器出故障,仍然可以保证数据不丢。
- 可靠性问题如何解决
可靠性问题一般侧重于数据的准确性,不能多发也不能少发。
多发问题,一般不会出现,出现的话,基本在于生产侧的多发消息活消费侧重复消费造成的.
引入MQ造成的数据可靠性问题,或者如何处理消息丢失问题?整个MQ的链路中,可能存在消息丢失的位置仅有三个,一个是生产端,一个是MQ自身,最后一个就是下游的消费者。
不同的MQ对此解决方案均不一样,需分别分析。
长时间未被出来的消息,会被放入死信队列。
ActiveMQ
事务、确认机制、消息持久化、集群
生产端
使用MQ提供的事务
消费者非事务方案下,使用ACK确认机制;
MQ自身
依赖于MQ高可用方案
增加持久化实现。
消费者
消费者非事务方案下,使用ACK确认机制;
使用事务;
RocketMQ
生产端
使用MQ提供的同步或异步发送方式,根据MQ返回的成功失败来确认是否重发
消费者非事务方案下,使用ACK确认机制;
MQ自身
依赖于MQ高可用方案,使用多master,多slave模式,异步刷盘同步复制。保证数据准确性及效率
增加持久化实现。
消费者
消费者非事务方案下,使用ACK确认机制;
使用事务;
系统复杂度提高
增加了MQ,引入了消息是否会重复消费,消息丢失问题。
数据一致性
引入MQ之后,单一事务会被分拆为多个事务。引入了分布式事务一致性的问题。
MQ顺序消费问题
RocketMQ
发送端将一个业务的消息按顺序发送至MessageQueue,producer一般用messageQueueSelector类来控制把消息发往某一个message queue。
消费端通过使用MessageListenerOrderly来解决message queue中的消息不被并发处理的问题,messageListenerOrderly并不是简单的禁止并发处理,而是采用了锁的机制,为每个message queue都加上了一把锁,要想消费到某一个message queue的消息,则必须获取到这把锁才行,从而保证了消费端消费消息的顺序。
MQ如何处理重复消费问题
幂等校验
上游不能重复下发
程序自己保证不重复创建消息,使用业务事务进行控制。下游不能重复消费
- 将消息信息入库,使用数据库、Redis进行消息存储。有消息过来使用唯一主键进行存储,每次进行校验是否存在,不存在继续处理,否则根据业务进行忽略或更新;
- 将消息信息入库,根据业务进行唯一主键约束,若有重复数据,则会报唯一主键冲突,也插入不了;
- 假如仅是redis的set操作,则无需处理。即时重复也不影响;
MQ如何处理消息丢失问题
- 消息持久化,集群;
- 增加消息确认机制;
死信队列、延时队列
- 死信队列也是一个消息队列,存放未被成功消费的队列;
a.消息被拒绝(basic.reject/ basic.nack)并且不再重新投递 requeue=false;
b.TTL(time-to-live) 消息超时未消费;
c.达到最大队列长度; - 延时队列存放过期未被消费的消息;