在前两天阿里的面试中,面试官问了几个关于MQ的问题:
What
1.为什么要使用 MQ
2.使用了 MQ 之后有什么优缺点
3.怎么保证 MQ 消息不丢失
4.怎么保证 MQ 的高可用性
其实大家平时可能也有用到MQ,但是可能对于 MQ 的理解仅仅停留在会使用 API 能实现生产消息、消费消息就完事了。可能很多人都没有对 MQ 的一些问题思考过。
怎么保证 MQ 消息不丢失
一条MQ消息从产生到消费,有没有可能失败?在哪些环节可能失败,如何处理?
1.消息生产失败
一般来说,从生产者到MQ中间件是通过网络调用的,是网络调用就有可能存在失败。消息队列通常使用确认机制,来保证消息可靠传递:当你代码调用发送消息的方法,消息队列的客户端会把消息发送到Broker,Broker接受到消息会返回客户端一个确认。只要Producer收到了Broker的确认响应,就可以保证消息在生产阶段不会丢失。有些消息队列在长时间没收到发送的确认响应后,会自动重试,如果重试再失败,就会一返回值或者异常方式返回给客户端。所以在编写发送消息的代码,需要正确处理消息发送返回值或者异常,保证这个阶段消息不丢失。
2.MQ处理存储失败
消息到达消息中间件之后,通常是会被存储起来的,只有被写入到磁盘中,消息才是真正地被存储,不会丢失。但是,大部分MQ中间件并不是收到消息就立马写入磁盘的,只是由于磁盘的写入速度相对于内存,现得慢得多得多,所以,像Kafka这样的消息系统,是会把消息写到缓冲区中,异步写入磁盘,如果机器在中途突然断电,是有可能会丢失消息的。为了解决这个问题,大部分的MQ都是采用分布式部署,消息会在多台机器上写入缓存中成功才会返回给业务方成功,由于多台机器同时断电的可能性较低,我们可以认为这是比较低成本又可靠的方案。
3.消费者处理失败
一般的MQ都有MQ重试机制,如果处理失败,就会尝试重复消费这个MQ。这个带来的问题就是,MQ可能已经成功消费了,但是在通知MQ中间件的时候失败了,这个时候带来的结果就是消息重复消费。同理,在生产者重试的时候,也会遇到消息重复消费的问题。这个时候,就要求我们尽量把接口设计得有幂等性,这个时候即便是重复消费,也不用担心什么问题了
怎样保证MQ的高可用性
RabbitMQ 是比较有代表性的,因为是基于主从做高可用性的,我们就以他为例子讲解第一种 MQ 的高可用性怎么实现。RabbitMQ有三种模式:单机模式、普通集群模式、镜像集群模式。
1.单机模式就是 demo 级别的,就是说只有一台机器部署了一个 RabbitMQ 程序。这个会存在单点问题,宕机就玩完了,没什么高可用性可言。一般就是你本地启动了玩玩儿的,没人生产用单机模式。
2.普通集群模式,意思是多台机器启动多个RabbitMQ实例,每个机器启动一个。你创建的queue,只会放在一个RabbitMQ实例上。但是每个实例会同步queue的元数据(可以理解queue的配置信息)。即使你消费的时候连接到了另一个实例,那么那个实例会从queue所在实例上拉取数据过来
其实并没有做到所谓的分布式,还是普通集群。因为会导致你要么消费者每次随机连接一个实例然后拉取数据,要么固定连接那个queue所在实例消费数据。前者有数据拉取的开销,后者会导致单实例性能瓶颈。
如果放queue的实例宕机了,则会导致其他实例都没有办法进行拉取。如果你开启了消息持久化,让RabbitMQ落地存储消息的话,消息不一定会丢。得等这个实例恢复了,然后才可以继续从这个queue拉取数据。
3.镜像集群模式才是所谓的rabbitmq的高可用模式,跟普通集群模式不一样的是,你创建的queue,无论元数据还是queue里的消息都会存在于多个实例上,然后每次你写消息到queue的时候,都会自动把消息到多个实例的queue里进行消息同步。
这样的话,好处在于你任何一个机器宕机了,没事儿,别的机器都可以用。
坏处在于这个性能开销也太大了吧,消息同步所有机器,导致网络带宽压力和消耗很重!
这么玩儿,就没有扩展性可言了,如果某个queue负载很重,你加机器,新增的机器也包含了这个queue的所有数据,并没有办法线性扩展你的queue那么怎么开启这个镜像集群模式呢?
这里简单说一下,避免面试人家问你你不知道,其实很简单rabbitmq有很好的管理控制台,就是在后台新增一个策略,这个策略是镜像集群模式的策略,指定的时候可以要求数据同步到所有节点的,也可以要求就同步到指定数量的节点,然后你再次创建queue的时候,应用这个策略,就会自动将数据同步到其他的节点上去了。
看完本文有收获?请转发分享给更多人
网友评论