1.为什么使用MQ?
使用MQ的好处:解耦,异步,削峰平谷
解耦:
当A系统生产关键数据,而且B,C,D系统需要A系统给它们发送数据,来进行下一步操作,此时A系统和BCD系统产生了严重的耦合,所有的操作和维护都要在A系统中进行,如果将A系统产生的数据放到MQ当中,让BCD系统需要的时候去消费,此时就解放了A系统,不用考虑调用成功,失败超时等情况,同时ABCD系统独立运行,后续新添加系统需要A系统的数据,也不需要去修改A系统的代码,达到了解耦的效果
异步:
一般互联网类企业,对用户的直接操作,一般要求每个请求在200ms以内完成。对于一个系统调用多个系统,在不适用mq的情况下,它执行完返回的耗时,是执行完所有系统所需时间的总和;使用mq进行优化后,执行的耗时,则是执行主系统的耗时,以及加上主系统发送数据到消息队列的耗时,大幅度提升高延时接口的性能,提升了用户体验.
削峰平谷:
一般MySQL的每秒请求最高在2000左右,用户访问量高峰期的时候涌入的大量请求,很可能将MySQL给打死,然后系统就挂掉,但是高峰期过了,请求量可能远远低于2000,所以这种情况去增加服务器就不值得,如果使用mq的情况,将用户的请求全部放到mq中,然后让系统去消费用户的请求,不要超过系统所能承受的最大请求数量,保证系统不会再高峰期挂掉,但此时可能有几十万或几百万请求积压在mq中,但是高峰期一过,系统还是按照最大请求数量进行处理请求,很快就能将积压请求处理完
使用MQ的缺陷:
2.你了解哪些MQ技术,ActiveMQ,RabbitMQ,RocketMQ,kafka
3.RabbitMQ 的使用场景有哪些?
抢购活动,削峰填谷,防止系统崩塌。
延迟信息处理,比如 10 分钟之后给下单未付款的用户发送邮件提醒。
解耦系统,对于新增的功能可以单独写模块扩展,比如用户确认评价之后,新增了给用户返积分的功能,这个时候不用在业务代码里添加新增积分的功能,只需要把新增积分的接口订阅确认评价的消息队列即可,后面再添加任何功能只需要订阅对应的消息队列即可。
4.如何保证MQ的高可用?
非分布式的MQ的高可用:
普通集群模式:实际queue保存在一个节点上,其他节点保存该queue的元数据,消费者连接某个节点,该节点去调用实际存放queue的节点,返回数据,说白了只是简单的提高了吞吐量而已.
缺陷在于:在集群内部产生大量的数据传输,高可用性没有,queue所在的节点宕机,服务就没法继续使用
镜像集群模式:每个节点上都保存了实际的queue的全部数据,消费者无论连接哪个节点,都能直接获取到数据.任何一个节点宕机,其他节点还存在queue的全部数据,消费者换个节点继续消费,这是高可用的模式
分布式MQ的高可用:
假设生产者往topic里写了三条数据,每条数据放在一个partition中,每个partition部署在一台机器中,每个partition都有一个或多个replica副本,其中有一个是leader,其他的是follower,只有leader能够向外提供数据的读写,生产者写入消息时,leader负责向follower同步消息,此时高可用架构就已经体现,如果一台leader宕机,会从它的follower中选举一个leader,继续向外提供服务
5.如何保证消息不被重复消费(如何保证消息消费时的幂等性)?
重复消费:
每个中间件它能够保证的是消息不丢失,但不能保证消息不被重复发送,所以接收方就要做幂等性的判断,防止消息重复消费,例如:支付金额的消息被消费了两次,那么所得到的钱数就是错误的,这种情况在系统中肯定是不允许存在的。
消费的幂等性(同样的操作,在一段时间内,只执行一次):
如果是往数据库里写入数据,就根据主键查一下,如果数据已经存在,就update
可以在把数据先存在set或者redis中,消费前,先去里面查看,数据是否已存在,已存在就丢弃这数据,比如说我们的订单系统,生成订单也会用到mq,此时订单id就是全局唯一的id,在写入数据库之前,就可以先把数据去redis中查询,如果redis中已经存在,则不进行消费操作,如果redis中不存在的话,就存在redis中,然后进行下一步操作.
在数据库中设置唯一约束,就不会导致重复数据的多次插入
6.如何保证消息不丢失
rabbitMQ:
消息丢失的情况:生产者写的消息在到中间件的网络传输过程中就丢了,或者是消息到了中间件,但是内部出错,消息没保存下来
中间件将消息保存下来,还没等到消费者消费完,就自己挂掉,导致消息丢失
消费者取到消息还没来得及消费就自己挂掉了,因为rabbitMQ消费者开启了autoAck,在消费数据还没成功时,就已经向中间件发送完成的信息,此时消费者挂掉,就会消息丢失
解决方案:
生产者消息丢失,可以通过开启事务功能,如果消息没有发送成功,发生异常就回滚,然后重试发送消息,发送成功就提交事务,这个的缺陷就是阻塞式的,降低吞吐量,耗费性能;如果是rabbitMQ可以开启confirm模式,它能给每次写的消息都分配一个唯一的id,如果写入到rabbitMQ中,rabbitMQ就会回传一个ack消息,如果没有就会会挑一个nack接口,告诉你消息接收失败,你可以重试,confrim机制是异步的,效率会高很多
关于中间件的数据丢失,可以开启中间件的持久化,将消息持久化磁盘中,中间件挂了恢复之后自动读取之前存储的数据.
消费者数据丢失,关闭rabbitMQ的autoACK机制,自己手动提交完成信息
7.如何保证消息的顺序性
rabbitMQ为多个消费者开辟多个queue队列(先进先出),将保证操作顺序的消息发布到同一个队列中去,操作这个队列的消费者会一个一个消息去处理,因为队列这种结构是先进先出的类型,所以保证的数据的顺序性。
8.消息大量积压怎么解决?
9.RabbitMQ 节点的类型有哪些?
磁盘节点:消息会存储到磁盘。
内存节点:消息都存储在内存中,重启服务器消息丢失,性能高于磁盘类型。
10.rabbitmq如何确认消息一定发送到了消息中间件中呢?
消息发送到server是先通过交换机,再到消息队列,只要数据到队列,那么此消息肯定是发送成功的.
第一步确定消息发送到了交换机.
使用发送方确认机制来判断消息是否发送成功.
第二步确认交换机把消息路由到消息队列.
使用失败回调来判断消息是否发送成功.
只有两步都成功,此消息才是发送成功的.
11.rabbitmq的集群
集群分为普通集群和镜像集群.
普通集群:
普通集群,他会把所有节点的交换机信息和队列的元数据分为两种(队列数据分为两种: 一种为队列里面的消息,另一种是队列本身的信息, 后者被称为元数据.)进行复制,确保所有的节点都有一份.
镜像集群:
在普通集群的基础上,把所有的队列数据完全同步(对性能有一定的影响)当对数据可靠性要求高时,可以使用镜像模式.
镜像集群实现有两种方式: 一种是直接在管理台控制,一种是在声明队列的时候控制.
配置集群还设计到节点信息, 有内存节点和磁盘节点,如果对队列有修改的情况下,必须有磁盘节点,用来保存信息,内存节点断电后,信息就消失,无法保存. 默认就是磁盘节点, 设置 --ram为内存节点.