当前位置:   article > 正文

Java面试——RabbitMQ专题_rabbitmq多个消费者监听一个队列会重复消费吗

rabbitmq多个消费者监听一个队列会重复消费吗

1、RabbitMQ支持的消息模式?

  • 简单模式: 生产发送消息至指定队列,消费者监听该队列消息
  • 工作队列模式
    - 生产发送消息至指定队列,多个消费者监听一个队列
    - 消息只会被消费一次
    - 默认平均消费
    - 可设置能者多劳模式
  • 发布订阅-广播
    - Fanout Exchange
    - 交换机接收消息后,广播给所有绑定队列
  • 发布订阅-路由
    - Direct Exchange
    - 交换机接收消息后,匹配对应的路由,发送给满足条件队列
  • 发布订阅-主题
    - Topic Exchange
    - 交换机接收消息后,匹配对应的路由,发送给满足条件队列
    - 路由支持通配符: # 0 或 多个单词 * 代表1个单词

2、RabbitMQ如何保证消息不丢失(mq如何保证可靠性)

对于rabbitmq的消息可靠性在项目里面我们必须要确保它的消息安全性,这个安全性呢,问题出现在于MQ如果在不做任何配置的情况下会导致消息丢失的。
丢失主要是三个方向,第一个是生产方,第二个是消费方,第三个呢就是broker,生产方默认是没有回调机制的,消费方也没有配置相关的回调并且没有设置失败策略,broker如果不加以强调,那么它在默认情况下,交换机,队列和消息都是瞬时态的,当服务器重启之后,瞬时态的数据在内存中将会丢失,所以对此我们需要一些相关的配置。
首先呢,我来说一下生产方,生产方呢首先要开启回调的开关,一共有两个开关,一个是confrim callback,一个是return callback,confirm callback是生产者到交换机失败与否会有一个回调,它这个回调不管是成功还是失败都会来回调,返回的是ACK和NACK,对发送消息的时候,我们需要对每一条消息都要保证它的消息可靠性,所以confirm callback局部的每次发消息都需要指定,第二个回调叫return callback,是交换机到队列成功与失败返回的回调函数,该函数内容只有在交换机给队列失败的情况下才会回调,如果失败了消费者是没有办法拿到消息的,所以对此的内容处理结果我们必须通过日志或者数据保存来得以操作,所以confirm callback和return callback需要配置,生产方呢主要是这两大方向。
接下来我们来聊聊broker,borker呢有三个持久化,分别是交换机持久化,队列持久化和消息持久化,由于我们的项目里面所使用的MQ的集成框架用的是SpringAMQP ,AMQP在使用它的代码的时候默认对这三者都进行了持久化,所以我不需要过多关心,它的持久化配置中有个关键性的内容,叫durable,还有消息里面有个叫delivery-mode配置它的持久化即可,所以框架已经做了,我就不需要在此进行配置。
第三个呢是消费方,消费方呢首先我们要在消费方接收到消息之后必须要返回ACK机制,所以要开启,对这个开启呢我们一般有三种方式,第一个就没有ACK第二个就是手动第三个呢就是Auto,那我们用的是AMQP所以我们使用的是Auto,Auto够简单,到底是返回ACK还是NACK呢我们直接抛异常即可,异常就是NACK,非异常那就是ACK,对当前NACK的消息呢我们会让消息重新入队,当开启了ACK机制如果消费方一致消费失败那么消费的消息就会一直重新入队,这样呢就会导致队列与消费者频繁的发生交互,失败重新发送新消息,消息重新入队,循环以往,这样会导致队列与消费者之间的通讯非常频繁,使得服务器的压力会非常大,因为它没有终止的情况,会一直重复调用,那么怎么解决这个问题呢,Spring框架就想到了这个方案,所以说呢,将队列与消费者之间的重试改为本地重试,对于本地重试呢我们可以设置重试的次数,重试的初始时间和重试的间隔时间等等,设置好之后呢,我们消费者重试的操作都在消费者本地来完成,但这里面会有一个问题,如果消费者本地重试达到最大次数,那么默认情况下这个重试的消息不会重新入队,这时候就会把消息丢弃掉,所以接下来我要设置一下消费者的失败策略,对于失败策略,SpringAMQP封装了三种策略方式,第一种呢叫拒绝回退,丢失消息,第二个呢叫重新回退,第三个呢叫重新入到错误交换机和错误队列,第一种方案不可取,因为消息会丢失掉,第二个重新回退,回退又会把消息重新发回过来,又会产生循环以往的重复消息的调用,所以这个呢也不可采用,我们将采用错误交换机和错误队列,原因就是采用该方式有一个好处,就是代表消费者已经重复最大次数,消费者已经没有办法消费了,就没有办法把消息重新回退了,重新放到一个地方单独来处理,管理,通过人工来进行处理,这就是我大致的一个方案。

3、MQ如何保证消息的幂等性(不重复消费)?

  1. 如果消费数据为了单纯的写入数据库,可以先根据主键查询数据是否已经存在,如果已经存在了就没必要插入了。或者直接插入也没问题,因为可以利用主键的唯一性来保证数据不会重复插入,重复插入只会报错,但不会出现脏数据
  2. 消费数据只是为了缓存到redis当中,这种情况就是直接往redis中set value了,天然的幂等性
  3. 针对复杂的业务情况,可以在生产消息的时候给每个消息加一个全局唯一ID,消费者消费消息时根据这个ID去redis当中查询之前是否消费过。如果没有消费过,就进行消费并将这个消息的ID写入到redis当中。如果已经消费过了,就无需再次消费了。

4、MQ出现消息堆积如何解决?

我在实际的开发中,没遇到过这种情况,不过,如果发生了堆积的问题,解决方案也所有很多的
第一:提高消费者的消费能力 ,可以使用多线程消费任务
第二:增加更多消费者,提高消费速度,
使用工作队列模式, 设置多个消费者消费消费同一个队列中的消息。
第三:扩大队列容积,提高堆积上限
可以使用RabbitMQ惰性队列,惰性队列的好处主要是:
①接收到消息后直接存入磁盘而非内存
②消费者要消费消息时才会从磁盘中读取并加载到内存
③支持数百万条的消息存储

5、MQ如何解决有序性问题?

  • 创建多个关联队列,每个队列只有一个消费者,通过业务主键ID取模,保证同一个id数据,发送到固定队列中
  • 就一个队列,对应一个消费者,然后这个消费者内部用内存队列(就是List)做排队,然后分发给底层不同的thread来处理,此方案可以支持高并发。

实际消费者的数量是受限的,不会仅仅因为消息消费太慢而去增加消费者实例的数量,所以通过方案2的方式,可以在不增加实例数量的前提下,加快消息消费的速度。

6、RabbitMQ的高可用机制?

我们当时项目在生产环境下,使用的集群,当时搭建是镜像模式集群,使用了3台机器。
镜像队列结构是一主多从,所有操作都是主节点完成,然后同步给镜像节点,如果主节点宕机后,镜像节点会替代成新的主节点,不过在主从同步完成前,主节点就已经宕机,可能出现数据丢失。
如果出现丢数据我们可以采用仲裁队列,与镜像队列一样,都是主从模式,支持主从数据同步,主从同步基于Raft协议,强一致。并且使用起来也非常简单,不需要额外的配置,在声明队列的时候只要指定这个是仲裁队列即可。

7、mq中的死信队列是什么?

我们当时的xx项目有一个xx业务,需要用到延迟队列,其中就是使用RabbitMQ来实现的。
延迟队列就是用到了死信交换机和TTL(消息存活时间)实现的。
如果消息超时未消费就会变成死信,在RabbitMQ中如果消息成为死信,队列可以绑定一个死信交换机,在死信交换机上可以绑定其他队列,在我们发消息的时候可以按照需求指定TTL的时间,这样就实现了延迟队列的功能了。
我记得RabbitMQ还有一种方式可以实现延迟队列,在RabbitMQ中安装一个死信插件,这样更方便一些,我们只需要在声明交互机的时候,指定这个就是死信交换机,然后在发送消息的时候直接指定超时时间就行了,相对于死信交换机+TTL要省略了一些步骤。

8、MQ如何实现延迟消费?

  1. 基于死信队列方式

创建一个交换机和一个队列存放信息,并且绑定死信交换机和队列,发送信息设置过期时间,过期后消息会进入到死信交换机中,然后被消费,达到延迟效果。

  1. 基于延时插件方式:
  • 安装延时插件
  • 创建延时交换机direct 交换机 属性: delayed true
  • 发消息时,在消息头 setHeader(“x-delay”, 10000)
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/549599
推荐阅读
相关标签
  

闽ICP备14008679号