MQ收发消息的大概流程
生产消息,消息存储,消息消费
MQ的如何保证消息100%不丢失(数据一致性问题)
哪些环节丢失消息
1.生产者没有发送数据到MQ
2.MQ没有开启数据持久或宕机等原因丢了
3.消费者获取消息后没有及时消费,宕机了
如何检测消息丢失
每条生产消息都应该配置一个全局唯一ID和消费状态,可以存放在mysql或redis中,作为跟踪检测的依据
如何确保消息不丢失
- rabbitmq:
生产者:1. 开启事务,会产生同步阻塞影响吞吐量,2. 开启confirm模式(异步非阻塞),
MQ:1. 开启queue持久化,2. 开启消息持久化,deliveryMode设置为2,
消费者:关闭rabbitmq的自动ack模式,手动调用ack
- kafka:
生产者:关闭自动提交offset,在自己处理完毕之后手动提交offset,这样就不会丢失数据
MQ:
给topic设置 replication.factor参数:这个值必须大于1,表示要求每个partition必须至少有2个副本。
在kafka服务端设置min.isync.replicas参数:这个值必须大于1,表示 要求一个leader至少感知到有至少一个follower在跟自己保持联系正常同步数据,这样才能保证leader挂了之后还有一个follower。
在生产者端设置acks=all:表示 要求每条每条数据,必须是写入所有replica副本之后,才能认为是写入成功了
在生产者端设置retries=MAX(很大的一个值,表示无限重试):表示 这个是要求一旦写入事败,就无限重试
消费者:如果按照上面设置了ack=all,则一定不会丢失数据,要求是,你的leader接收到消息,所有的follower都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试,重试无限次
MQ怎么解决重复消费的问题
关键是解决消费的幂等性。每条生产消息都应该配置一个全局唯一ID和消费状态,存放在mysql或redis中。在消费端的业务代码中可以通过中间件或拦截器检查消息表中的状态是否已经被消费过。
MQ消息积压怎么解决
优先解决先上问题,临时扩容消费端
通过日志排查,为何会积压消息
优化业务逻辑,或根据实际情况选择扩容
MQ如何选择
kafka,只做消息持久化,不删消息,消费端需要维护offset,特点是高并发高吞吐,适合日志队列
rabbitmq,数据安全可靠,队列模型多样,适合各种业务场景
rocketmq,数据可靠,阿里维护,比较成熟
延迟队列怎么实现
场景:
- 订单成功后,在 30 分钟内没有⽀付,⾃动取消订单
- 如果订单⼀直处于某⼀个未完结状态时,及时处理关单,并退还库存
- ⽀付成功后, 2 秒后查询⽀付结果
方案:
rabbitmq TTL+ 死消息队列:基本原理是对消息设置过期时间,过期的消息会放置到死消息队列,专门处理死消息队列视作延迟队列
redis 有序集合 数据安全不太可靠,集合中的数据量大时,影响速度。基本原理是插入时间戳为score的消息,让zset按时间自动排序。消费时判断时间戳,到时间则处理,没到时间则轮询等待。
kafka 时间轮:原理比较复杂,基本原理使用一个数组模拟时间钟,键作为秒针,把消息任务存放在某个键值内,程序定时取该数组键值执行然后。
为什么MQ能实现高吞吐,高并发,以及优势
业务解耦:各业务模块可以专心负责自己的业务,降低运维和调试的难度。
数据最终一致性:通过记录和补偿的方式,即使环节中出了问题,也可以通过补救,保证数据最终落地,可以作为分布式事务的替换方案
风流与错峰:面对瞬间袭来的高并发数据写入的要求,传统关系型数据库是无法抵御的,因此需要通过排队的方式缓解写入压力
发布订阅:比传统关系型数据库具备更强的,读取负载能力
消息中间件中的队列模型与发布订阅模型区别
JMS(java消息服务)规范中定义了两种模型
点对点:消息⽣产者⽣产消息发送到queue中,然后消息消费者从queue中取出并且消费消息。这⾥要注意:
消息被消费以后,queue中不再有存储,所以消息消费者不可能消费到已经被消费的消息
发布与订阅:消息⽣产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点⽅式不同,发布到topic的消息会被所有订阅者消费。
rabbitmq ,kafka等在其基础上扩展成了routing + 多个topic/queue 实现更强的高负载高可用
kafka只⽀持消息持久化,消费端为拉模型,消费状态和订阅关系由客户端端负责维护,消息消费完后不会⽴即删除,会保留历史消息。因此⽀持多订阅时,消息只会存储⼀份就可以了。