RocketMQ高频面试问题
痛点一
- 同步接口调用的方式总耗时比较长,非常的影响用户体验,特别是在网络不稳定的情况下,极容易接口超时问题
解决方案:
- 同步接口,使用MQ之后将同步改为异步,能够显著减少系统的响应时间
- 比如一个独立消息生产者,完成自身业务后将业务结果发送到消息中间件中,就相当于完成的相关的业务逻辑了,不需要等待消费者消费完返回值,最中能独立完成业务功能
痛点二
- 微服务中,多个业务系统进行拆分后,系统之间的耦合性太高,导致的其中一个微服务挂掉之后,其他的服务也会收到影
解决方案:
- 某个服务出现问题后,不会影响其他服务的继续执行,出问题期间,所需要消费的消息会保存在消息中间件中,等待服务上线后重新消费消息,
- 这样就把之前复杂的服务之间复杂的依赖改为只依赖MQ进行消息发送存储,及获取消费,显著降低了服务之间的依赖耦合
痛点三
- 秒杀服务中 时间大量请求涌现进来,请求达到峰值,这样很容易就把服务器打崩溃掉
消息的削峰
- 请求将消息发送给MQ,一次性想拿多少消息是可以通过MQ进行设置的,过多的消息就会保存在MQ中,服务按照自己的节奏啦消费消息
MQ自身存在的问题
消息重复消费的问题
- 生产者产生重复的消息
- 消息消费者确认是失败了
- 首先发送消息个消费者,消费者消费了消息,将消费的结果返回去给MQ(告诉MQ我已经消费消息了,MQ知道后对已消费消息进行标记),但是如果返回消费结果给MQ时失败了,MQ就再次投递消息,消息就重复消费了,
- 消息消费者确认超时也会重复生产
- 业务系统主动发起重试
解决方案
-
幂等性
-
幂等性设计,
-
建立一张表(消费信息表),唯一索引MessageId,保证这个信息的唯一性,查询一下这个消息是否被处理过,如果处理过了 ,在判断客户是否重复购买时生成订单进行的兜底的方案使用过该功能
-
-
还有就是使用状态机的方式,当使用修改状态的SQL语句
-
UPDATE student SET status = “C” WHERE id =#{id} and status = “B”
-
需要知道的是尽管在高并发的情况下,两条线程同时想要执行该条语句因为SQL中有行锁的机制,那么也是需要进行排队进行的,当状态改变后,另一个线程就无法进行执行了
-
-
-
ACK的概念,多种MQ
- 多种MQ的专业术语,ACK在rockMQ的中也有实现,只是封装好了
- 就是消息消费后会返回在一个值告诉MQ已经消费了消息了做出标记,就是消费者这一端应答MQ的已经消费消息
- 集成Springboot时已经进行封装了,别的MQ需要ACK响应
数据一致性问题
- mq的消费者业务处理异常的话就会出现数据一致性问题.比如一个完整的
- 比如下单成功,积分服务积分增加,
- 订单创建了,积分未增加,积分消息发送失败,
- MQ保证了消息的可靠性,失败了就会重复发送,总共会发送16次,都失败后进入死信队列,
- 死信队列:对死信队列的消息进行监听,后续人工补偿操作
- 所以这种情况只需要保证消息的幂等性就行,
- 订单创建时出问题了,增加积分确成功了,毕竟是异步发送的消息
-
使用到事务消息
-
-
情况一:
- 发送一个半消息,不能被消费者订阅到,只是停在MQ的服务器中
- MQ服务端告知MQ发送方会通知发送成功
- MQ发送方就会去执行本地事务,事务执行完毕后将结果告知MQ服务端,是提交事务还是回滚事务就看MQ发送方的事务执行结果了
- 事务失败那么MQ发送就会回滚MQ服务端的事务,将半消息删除掉,不投递信息
- 事务成功提交就会补全MQ服务端中半消息,将消息投递给MQ的订阅方
-
情况二:
- 发送半消息时直接失败了,那就没有后戏了
-
情况三:
- ①发送半消息时成功,②MQ服务端没有给回馈
- 这时半消息待到一定时间发现没有反馈这时⑤去到MQ发送方去查询事务状态,此时MQ发送方由于没收到③执行事务的通知,⑥检查事务属于未执行状态,⑦Rollback将半消息删除掉了,消息一致未执行
-
情况四:
- ①发送半消息时成功,②MQ服务端消息发送到MQ发送方
- ③MQ发送方执行本地事务成功,
- ④失败了,这时半消息待的时间够了,⑤去到MQ发送方去查询事务状态
- ⑥MQ发送方检查本地事务发现是成功的,⑦MQ发送方提交事务,MQ服务端补全半消息,投递消息给消息订阅者
-
后续有的情况可以自己思考一下比如⑦废废了怎么办
-
- 订单创建了,积分未增加,积分消息发送失败,
顺序消息
- 每一个Topic中RocketMQ中默认所有四个写队列,四个读队列
- 为高性能使用的四个队列而不是一个,迎来的是并发问题
- 同一个订单号的业务放置在一个队列中
- 利用订单号取模的方式进行一的队列的分配,这样也会出现别的订单掺杂到同一个队列中,但是这个是不影响的只需要保证,同一个订单中的消息是由逻辑顺序即可
- 具体实现就是将重写RockMQ中的消息选择器,按照自己的想要的队列方式去,数据类型(决定的消息的排序,List集合保证有序的消息)
- 集成Springboot消费者消费时只需要在注解中更换消费模式为orderly,单线程消费消息(一个线程绑定的是一个队列)
消息堆积问题
- 消费消息的速度赶不上消息生产的速度,从而影响
- 增加消费者的集群数量
- 消息消息的服务出了故障,消费消息速度小于平时消费消息速度
- 定位是不是消费者内部问题,把问题解决了,变回正常速度
- 由于前面积压的问题题
- 此时恢复了正常速度,目目前队列中的信息是否重要,不重要的直接删除掉,
- 消息比较重要,增加消费者集群数量,把积压消息消费后再下线
- 怎么判断是否重要
- 通过滤的方式,比如生产者使用tag或者SQL92的方式重要消息进行标记,消费者通过标记进行筛选信息处理
- 突然请求的量剧增,业务高峰期,导致产生消息比平时多
- 采取限流
判断是否重要- 通过滤的方式,比如生产者使用tag或者SQL92的方式重要消息进行标记,消费者通过标记进行筛选信息处理
- 采取限流
- 突然请求的量剧增,业务高峰期,导致产生消息比平时多
- 采取限流
- 增加消费者的集群数量