RabbitMQ高频面试题

RabbitMQ的使用场景

  • 异步发送消息(验证码、短信、邮件…)
  • mysql、redis、es之间的数据同步
  • 分布式事务
  • 削峰填谷

面试题:RabbitMQ如何保证消息不丢失

RabbitMQ高频面试题_第1张图片消息丢失原因

  • 生产者发送的消息未到达交换机
  • 交换机未把消息路由到队列
  • mq服务器宕机,队列中的消息丢失
  • 消费者服务宕机,未接收到消息

发送端消息丢失解决方案

生产者确认机制
rabbitmq提供了publisher confirm机制来避免消息发送到mq过程中丢失。返回一个结果给生产者,表示消息是否接收成功。

消息接收失败后处理方案

  • 回调方法即时发送
  • 记录日志
  • 保存到数据库表定时重发,发送成功后删除表数据

交换机持久化

    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange("myFanout.fanout",true,false);
    }

队列持久化

    @Bean
    public Queue simpleQueue(){
        return 
        // 使用QueueBuilder.durable创建的队列是持久化的
        QueueBuilder.durable("simpleQueue").build();
    }

消息持久化
mq默认是消息存储在内存中,开启持久化功能可以保证缓存在mq中的消息不丢失。

String message = "hello";
Message msg = MessageBuilder.withBody(message.getBytes(StandardCharsets.UTF_8)) // 设置消息体(将消息转换成byte)
               .setDeliveryMode(MessageDeliveryMode.PERSISTENT) // 将消息持久化
               .build();

消费者确认机制
rabbitmq支持消费者确认机制,消费者处理消息后向mq发送ack回执,mq收到ack后将消息删除。springAMQP则支持三种确认方式。

  • manual :手动ack,需要在业务代码执行完成后,手动调用api发送ack
  • auto : 自动ack,有spring监听代码是否发生异常,没有异常则发送ack,有异常则发送nack。(推荐使用)
  • none :关闭ack,mq假定消息者收到消息后就会处理成功,投递消息后将消息删除

我们可以利用Spring的retry机制,在消费者出现异常时利用本地重试,设置重试次数,当次数达到了以后,如果消息依然失败,将消息投递到异常交换机,交由人工处理。
RabbitMQ高频面试题_第2张图片

rabbitmq消息重复消费怎么解决

rabbitmq消息重复消费产生原因:

  • 网络波动
  • 消费者服务宕机

RabbitMQ高频面试题_第3张图片解决方案:

  • 给每条消息设置一个唯一的id标识(支付id、订单号、文章id…)
  • 幂等方案【分布式锁、数据库锁(乐观锁、悲观锁)】

**面试回答:**嗯,这个我们还真遇到过,是这样的,我们当时消费者是设置了自动确认机制,当服务还没来得及给MQ确认的时候,服务宕机了,导致服务重启之后,又消费了一次消息。这样就重复消费了。
因为我们当时处理的支付(订单|业务唯一标识),它有一个业务的唯一标识,我们再处理消息时,先到数据库查询一下,这个数据是否存在,如果不存在,说明没有处理过,这个时候就可以正常处理这个消息了。如果已经存在这个数据了,就说明消息重复消费了,我们就不需要再消费了。

rabbitmq死信交换机(延迟队列)有了解过吗

场景:超时订单、限时优惠、定时发布…

死信交换机
当一个队列中的消息满足下列情况之一时,可以成为死信(dead letter) :

  • 消费者使用basic.reject或 basic.nack声明消费失败,并且消息的requeue参数设置为false
  • 消息是一个过期消息,超时无人消费
  • 要投递的队列消息堆积满了,最早的消息可能成为死信

如果该队列配置了dead-letter-exchange属性,指定了一个交换机,那么队列中的死信就会投递到这个交换机中,而这个交换机称为死信交换机(Dead Letter Exchange,简称DLX)。

RabbitMQ高频面试题_第4张图片

    @Bean
    public Queue ttlQueue(){
        Map<String,Object> map = new HashMap<>();
        //设置死信队列属性
        map.put("x-dead-letter-exchange","order_exchange_delay");
        map.put("x-dead-letter-routing-key","dlx.order.get");
        map.put("x-message-ttl",5000);
        return QueueBuilder.durable("dl.queue").withArguments(map).deadLetterExchange("dl.direct").build();
    }

TTL
TTL,也就是Time-To-Live。如果一个队列中的消息TTL结束仍未消费,则会变为死信,TTL超时分为两种情况:

  • 消息所在的队列设置了存活时间
  • 消息本身设置了存活时间

如果这两种情况设置了TTL,那么以时间小的为准

消息堆积如何解决

什么是消息堆积?
当生产者发送消息的速度超过了消费者处理消息的速度,就会导致队列中的消息产生堆积,若队列存储的消息达到上限,那么队列外的消息就会成为死信,消息可能会被丢弃。
解决消息堆积的方案:

  • 增加消费者服务,提高消费者处理消息的速度
  • 在消费者服务内开启线程池加快消息处理的速度
  • 扩大消息队列的容积,提高堆积上限

惰性队列
惰性队列的特点如下:

  • 消息保存到磁盘
  • 消费者要消费时才会从磁盘读取加载到内存,效率会降低
  • 支持数百万的消息存储
    @Bean
    public Queue simpleQueue(){
        return 
        // 使用QueueBuilder.durable创建的队列是持久化的
        QueueBuilder.durable("simpleQueue")
        // 开启 x-queue-mode为lazy 
        .lazy()
        .build();
    }

你可能感兴趣的:(java-rabbitmq,rabbitmq,java)