你在系统里用着消息队列,但要是被面试官问一句“为什么要引入MQ,直接读写数据库不行吗”,答不上来可就尴尬了。别慌,咱们今天就借着几道高频面试题,把消息队列(MQ)的原理和实战场景掰扯清楚,全是干货。
解耦异步削峰三大核心场景
引入MQ主要是为了解决三个核心问题。先说解耦,比如A系统要发数据给BCD三个系统,如果直接接口调用,C系统一挂,A系统就得跟着报错。用了MQ,A系统只管把消息丢进队列,不用关心其他系统是否在线。
异步场景更常见,用户下单后,你既要发短信又要送积分,同步处理耗时翻倍。通过MQ异步处理,用户端一秒响应,体验直接拉满,后端慢慢消费就行。
削峰填谷是抗住高并发的关键。秒杀活动时流量瞬间暴涨,直接写数据库肯定扛不住。让请求先冲进MQ,后端系统按自己的最大能力慢慢消费,保证系统不崩溃。
引入MQ后数据一致性的挑战
系统解耦爽了,但数据一致性就成了新问题。原来A系统直接调用,要么全成功要么全失败。现在A把消息发出去就返回成功,结果B和D处理成功,C却写库失败,数据就不一致了。
这种情况下,要么用分布式事务,要么基于MQ实现最终一致性。在实际电商项目里,2023年双十一期间,我们就遇到过订单状态和库存扣减不一致的情况,后来通过MQ的事务消息才解决。
高可用方案镜像集群模式
面试官肯定要问MQ自身的高可用。单机模式就不用说了,一挂全完蛋。普通集群模式其实也不可靠,queue只在一个节点上,其他节点消费时还得去拉数据,那个节点宕机消息就取不出来了。
真正高可用得用镜像集群模式。在RabbitMQ里配置策略,要求数据同步到所有节点。这样任何一个节点宕机,消费者立马切换到其他节点,消息不丢服务不停,保证99.99%的可用性。
消息积压的三阶段处理法
消息积压是生产环境最常见的故障。消费端挂了或者消费速度太慢,消息在MQ里堆成山,存储爆了,老数据就开始过期删除。
针对这个问题,事前要评估好消费者的处理能力和队列长度;事中如果发现积压,赶紧临时扩容消费者;事后如果数据丢了,得准备脚本从数据库或日志里重建数据。去年我们有个业务就积压了上亿条消息,靠临时加机器硬扛过去的。
消息零丢失的三大保障
消息丢失是个大坑。生产者发消息时网络闪断丢了;MQ自己挂了内存里的数据丢了;消费者处理到一半重启了,消息也丢了。
生产端要开启confirm模式,每条消息分配唯一ID,收到MQ的回调才算发送成功。MQ本身必须开启持久化,创建queue时设置持久化,发消息时设置delivery mode为2。消费端关闭自动ack,业务处理完手动调用ack接口,这样任何环节都不会丢消息。
幂等性设计防止重复消费
网络抖动会导致MQ重复投递消息,如果你的业务逻辑不是幂等的,那就惨了。比如同样的钱扣了两次,或者同样的积分加了两次。
处理方式要看你操作的组件。操作数据库就用唯一主键,重复插入会报主键冲突。操作Redis的set操作本身幂等,不用处理。调用第三方接口,得先确认对方有没有去重机制,没有的话就自己在业务表里建个消息去重表,拿消息ID做唯一约束,保证只处理一次。
说到这里,不知道你在实际项目里遇到过最奇葩的消息重复消费案例是什么?欢迎在评论区分享你的踩坑经历,点个赞让更多同事看到,大家一起避坑。
