为什么使用MQ
解耦,异步,削峰
MQ优缺点
系统可用性降低
系统复杂度提高
一致性问题
Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什么区别?
直接看功能支持那一栏
![]()
小公司用RabbitMq延时性低,非分布式的,大公司用rokectmq吞吐量高,分布式的
kafka分布式的,功能较简单,大数据领域实时计算以及日志采集被大规模使用
RabbitMQ如何保证高可用的?
RabbitMQ是基于主从(非分布式)做高可用性的,
RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模式
没人生产用单机模式
缺点:1.可能会在RabbitMQ集群内部产生大量的数据传输
2.可用性几乎没有什么保障,如果queue所在的节点宕机了,就导致queue的数据丢失了,你就没办法消费了创建的queue,只会放在一个 RabbitMQ 实例上,但是每个实例都同步 queue 的元数据(元数据可以认为是queue 的一些配置信息,通过元数据,可以找到 queue 所在实例)。你消费的时候,实际上如果连接到了queue不在的实例,那么那个实例会从 queue 所在实例上拉取数据过来
优点:才是所谓的 RabbitMQ 的高可用模式,你任何一个机器宕机了,没事儿,其它机器节点还包含了这个 queue 的完整数据,别的 consumer 都可以到其它节点上去消费数据坏处:不是分布式的,如果这个queue的数据量很大,导致网络带宽压力和消耗很重创建的 queue,无论元数据还是 queue 里的消息都会存在于多个实例上RabbitMQ 有很好的管理控制台,就是在后台新增一个策略,这个策略是镜像集群模式的策略,指定的时候是可以要求数据同步到所有节点的,也可以要求同步到指定数量的节点
RabbitMQ,Kafka如何保证消息不重复(面试题)
比如你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入了, update 一下好吧。比如你是做redis的set的操作, 天然幂等性。如果上面两种情况还不行,准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。
RabbitMQ如何保证消息不丢失(面试题)
数据的丢失问题,可能出现在生产者、MQ、消费者中
生产者丢失:生产者将数据发送到 RabbitMQ 的时候,可能数据就在半路给搞丢了,因为网络问题啥的,都有可能。
就是生产者发送数据之前开启 RabbitMQ 事务channel.txSelect,然后发送消息,如果消息没有成功被 RabbitMQ 接收到,那么生产者会收到异常报错,此时就可以回滚事务channel.txRollback,然后重试发送消息;如果收到了消息,那么可以提交事务channel.txCommit。吞吐量会下来,因为太耗性能。同步的
可以开启confirm模式,在生产者那里设置开启confirm模式之后,你每次写的消息都会分配一个唯一的 id,然后如果写入了 RabbitMQ 中,RabbitMQ 会给你回传一个ack消息,告诉你说这个消息 ok 了。如果 RabbitMQ 没能处理这个消息,会回调你一个nack接口,告诉你这个消息接收失败,你可以重试,异步的
设置持久化有两个步骤:第一个是将queue的持久化标识durable设置为true,则代表是一个持久的队列 这样就可以保证 RabbitMQ 持久化 queue 的元数据,但是不会持久化 queue 里的数据。第二个是发送消息的时候将消息的 deliveryMode 设置为 2 ,就是将消息设置为持久化的,此时 RabbitMQ 就会将消息持久化到磁盘上去。必须要同时设置这两个持久化才行,RabbitMQ 哪怕是挂了,再次重启,也会从磁盘上重启恢复 queue,恢复这个 queue 里的数据。持久化可以跟生产者那边的 confifirm 机制配合起来,只有消息被持久化到磁盘之后,才会通知生产者ack 了,所以哪怕是在持久化到磁盘之前, RabbitMQ 挂了,数据丢了,生产者收不到ack ,你也是可以自己重发的。注意,哪怕是你给 RabbitMQ 开启了持久化机制,也有一种可能,就是这个消息写到了 RabbitMQ 中,但是还没来得及持久化到磁盘上,结果不巧,此时 RabbitMQ 挂了,就会导致内存里的一点点数据丢失。
消费端丢失:你消费的时候,刚消费到,还没处理,结果进程挂了,比如重启了,那么就尴尬了,
RabbitMQ 认为你都消费了,这数据就丢了
启用手动确认ack模式可以解决这个问题
简单来说,就是你关闭 RabbitMQ 的自动ack,可以通过一个 api 来调用就行,然后每次你自己代码里确保处理完的时候,再在程序里ack一把,
手动确认模式,如果消费者来不及处理就死掉时,没有响应ack时会重复发送一条信息给其他消费者;如果监听程序处理异常了,且未对异常进行捕获,会一直重复接收消息,然后一直抛异常;如果对异常进行了捕获,但是没有在finally里ack,也会一直重复发送消息(重试机制)。
如何保证消息的顺序性
RabbitMQ:同一个queue里的消息一定是顺序消息的
一个 queue,多个 consumer保证一个 queue,一个 consumer,然后这个消费者内部用内存队列做排队,保证顺序性
或者拆分成多个queue,生产者保证顺序性
消息积压如何处理
临时紧急扩容:然后将现有 cnosumer 都停掉新建一个 topic , partition 是原来的 10 倍,临时建立好原先 10 倍的 queue 数量。 然后写一个临时的分发数据的 consumer 程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的 10 倍数量的 queue 。等快速消费完积压数据之后,重新用原先的 consumer 机器来消费消息。
MQ中消息失效如何处理
假设你用的是 RabbitMQ , RabbtiMQ 是可以设置过期时间的,也就是 TTL 。如果消息在 queue 中积压超过一定的时间就会被 RabbitMQ 给清理掉,这个数据就没了。那这就是第二个坑了。这就不是说数据会大量积压在 mq 里,而是大量的数据会直接搞丢。就是批量重导,就是大量积压的时候,我们当时就直接丢弃数据了,然后等过了高峰期以后,比如晚上12 点以后,用户都睡觉了。这个时候我们就开始写程序将丢失的那批数据,一点一点的查出来,然后重新灌入 mq 里面去,把白天丢的数据给他补回来。假设 1 万个订单积压在 mq 里面,没有处理,其中 1000个订单都丢了,你只能手动写程序把那 1000 个订单给查出来,手动发到 mq
mq消息队列块满了如何处理
临时写一个consumer 程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入 queue。然后走第二个方案,将丢失的那批数据,一点一点的查出来,然后重新灌入 mq 里面去。
Kafka如何保证高可用的?
后面有问到再补充ba