1、为什么使用消息队列
问题用意:
其实就是想问一下消息队列有哪些使用场景,你项目中什么业务场景用到了消息队列,有什么技术挑战。使用MQ后给你带来了什么好处
规范回答:
消息队列的常见使用场景很多,但比较核心的有3个:解耦、异步、流量削峰填谷
1. 解耦
A系统发送数据到B系统再发送数据到C系统。A要时刻考虑B或C会不会挂掉,B也的考虑C会不会挂掉。但使用MQ的话,B将消息发送给MQ后,就不会再考虑后面的事情了。
所以你需要考虑一下自己的项目中是否有类似的场景,就是一个系统或一个模块调用了多个系统或模块,并且互相之间的调用很复杂,维护起来麻烦。并且这个调用也不是必须同步调用接口的,那就可以MQ给它异步化解耦。
2. 异步
A系统接收了一个请求,需要在自己本地写库,还需要在B、C系统写库。自己本地写库要30ms,B、C写库分别需要300ms、450ms。那最终请求总时延就是30+300+450=780ms。异步后,B、C系统分别写库的时间,A系统就不再考虑了。
3.削峰
每天0点到16点,B系统风平浪静,每秒并发请求数是1W个。结果每次一到16点到23点,每秒并发请求数突然会暴增到30万条。但C系统最大处理能力就只能是每秒钟1w请求。这时候就需要添加MQ进行流量的削峰,让系统可以平缓的处理突增的请求
2、消息队列有什么优点和缺点
优点:
- 解耦
- 异步
- 削峰
缺点:
-
系统可用性降低
系统引入的外部依赖越多,越容易挂掉,本来A系统调用BCD三个系统的接口就好了,ABCD四个系统好好的,没啥问题,你偏加个MQ进来,万一MQ挂了怎么办?MQ挂了,整套系统崩溃了,业务也就停顿了。
解决方法:MQ可以加集群,防止MQ挂了而影响整个系统的性能 -
系统复杂性提高
添加MQ后,如何保证消息没有重复消费?如何处理消息丢失的情况?如何保证消息传递的顺序性
-
一致性问题
A系统处理结束后直接返回成功,可能都以为这个请求就成功了;但是问题是,要是BCD三个系统那里,BD两个系统写库成功了,结果C系统写库失败了,就会导致数据不一致。
所以消息队列实际是一种非常复杂的架构,引入它有很多好处,但是也需要针对它带来的坏处做各种额外的技术方案和架构来进行规避。
3、常见消息队列比较
4、如何解决重复消费
消息被重复消费,就是消费方多次接收到了同一条消息。根本原因就是,第一次消费完之后,消费方给 MQ 确认已消费的反馈,MQ 没有成功接受。比如网络原因、MQ 重启等。所以MQ 是无法保证消息不被重复消费的,只能业务系统层面考虑。
不被重复消费的问题,就被转化为消息消费的幂等性的问题。幂等性就是指一次和多次请求的结果一致,多次请求不会产生副作用。
保证消息消费的幂等性可以考虑下面的方式:
- 给消息生成全局 id,消费成功过的消息可以直接丢弃。消息中保存业务数据的主键字段,结合业务系统需求场景进行处理,避免多次插入。是否可以根据主键多次更新而并不影响结果等
5、让你来设计一个消息队列,你会怎么设计
(1)数据存储角度:
理论上,从速度来看,分布式文件系统 > 分布式KV(持久化)> 数据库;而可靠性却截然相反,如果追求性能可以基于文件系统的顺序写。
(2)高可用角度:分区+复制+选举的思想
(3)网络框架角度:选用高效的Netty框架,producer 同步异步发送消息,consumer 同步异步接收消息。同步能够保证结果,异步能够保证性能。
6、有几百万消息持续积压几小时,说说怎么解决?
发生了线上故障,几千万条数据在MQ里积压很久。是修复consumer的问题,让他恢复消费速度,然后等待几个小时消费完毕。这是个解决方案。不过有时候我们还会进行临时紧急扩容。
一个消费者一秒是1000条,一秒3个消费者是3000条,一分钟是18万条。1000多万条,所以如果积压了几百万到上千万的数据,即使消费者恢复了,也需要大概1小时的时间才能恢复过来。
一般这个时候,只能操作临时紧急扩容了,具体操作步骤和思路如下:
(1)先修复consumer的问题,确保其恢复消费速度,然后将现有consumer都停掉。
(2)新建一个topic,partition是原来的10倍,临时建立好原先10倍或者20倍的queue数量。
(3)然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue。
(4)接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据。
这种做法相当于是临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据。等快速消费完积压数据之后,再恢复原先部署架构,重新用原先的consumer机器来消费消息。