消息中间件面试篇(RabbitMQ)

RabbitMQ

一、消息不丢失(RabbitMQ-如何保证消息不丢失)

在这里插入图片描述
我们当时MYSQL和Redis的数据双写一致性就是采用RabbitMQ实现同步的,这里面就要求了消息的高可用性,我们要保证消息的不丢失。主要从三个层面(生产者,mq,消费者)考虑

1.消息未发送到消息队列

这里需要提前配置

spring:
  # 配置rabbitMq 服务器
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    # 消息确认配置项
    # 确认消息已发送到交换机(Exchange)
    publisher-confirm-type: correlated
    # 确认消息已发送到队列(Queue)
    publisher-returns: true
    # 虚拟主机名称
    virtual-host: /
考虑是否发送到exchange

可以通过实现 ConfirmCallback函数,不管是否收到消息的一个回调方法,可以通过对ack进行确认来判断。

//消息发送到交换机确认回调(失败成功都会调用)
//CorrelationData correlationData消息相关数据, boolean ack是否发送成功, @Nullable String cause原因
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) ->{
   if (ack){
      log.info("消息发送成功");
   }else {
      log.info("消息发送失败,原因:{}",cause);
    }
} );
考虑是否发送到queue

可以通过实现 ReturnCallback函数,发送失败就会调用。

//3.消息从交换机发送到队列确认回调 (失败才调用)
rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
	log.info("returnCallback:     " + "消息:" + message);
	log.info("returnCallback:     " + "回应码:" + replyCode);
	log.info("returnCallback:     " + "回应信息:" + replyText);
	log.info("returnCallback:     " + "交换机:" + exchange);
	log.info("returnCallback:     " + "路由键:" + routingKey);
});

2.开启持久化功能

确保消息未消费前在队列中不会丢失,其中的交换机、队列、和消息都要做持久化

 	//声明交换机,且持久化
   	@Bean(PAYNOTIFY_EXCHANGE_FANOUT)
    public FanoutExchange paynotify_exchange_fanout() {
        // 三个参数:交换机名称、是否持久化、当没有queue与其绑定时是否自动删除
        return new FanoutExchange(PAYNOTIFY_EXCHANGE_FANOUT, true, false);
    }
    //支付通知队列,且持久化
    @Bean(PAYNOTIFY_QUEUE)
    public Queue course_publish_queue() {
        return QueueBuilder.durable(PAYNOTIFY_QUEUE).build();
    }
    //消息持久化 发送前的消息封装
	Message message = MessageBuilder
                    .withBody(JSON.toJSONString(map).getBytes(Constants.UTF_ENCODING))
                    .setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN)
                    .setDeliveryMode(MessageDeliveryMode.PERSISTENT)
                    .build();

3.1开启消费者确认机制

为auto,由spring确认消息处理成功后完成ack,当然也需要设置一定的重试次数,我们当时设置了3次,如果重试3次还没有收到消息,就将失败后的消息投递到异常交换机,交由人工处理

spring:
  rabbitmq:
    listener:
      simple:
        retry:
          enabled: true
          max-attempts: 3 #重试次数

注意:一定要开启确认机制为auto ,出现异常一定要抛出!

3.2 还一种方案:消费者手动确认

spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: manual 手动
@RabbitListener(queues = "queue")
public void listen(String object, Message message, Channel channel) {
    long deliveryTag = message.getMessageProperties().getDeliveryTag();
    log.info("消费成功:{},消息内容:{}", deliveryTag, object);
    try {
        /**
         * 执行业务代码...
         * */
        channel.basicAck(deliveryTag, false);
    } catch (IOException e) {
        log.error("签收失败", e);
        try {
        //记录日志到数据库中 或者通知工作人员
            channel.basicNack(deliveryTag, false, false);
        } catch (IOException exception) {
            log.error("拒签失败", exception);
        }
    }
}

注意:如果开启 channel.basicNack(deliveryTag, false,true),重复机制如果是代码bug会造成死循环。

二、消息重复消费

这个我们还真遇到过,是这样的,我们当时消费者是设置了自动确认机制,当服务还没来得及给MQ确认的时候,服务宕机了,导致服务重启之后,又消费了一次消息。这样就重复消费了。
因为我们当时处理的支付(订单|业务唯一标识),消息中会携带有一个的唯一标识(选课id),插入用户课程表的时候由于是唯一键即使重复消费也不影响最终结果。
注意:新增使用唯一标识,修改的话可以使用分布式锁,数据库锁(乐观锁)。

三、如果有100万消息堆积在MQ , 如何解决 ?

三种思路:

  • 添加更多的消费者,提高消费速度。
  • 适当增加prefetch的数量,也可以使用多线程concurrent增加线程数。
  • 扩大队列容积,采用惰性队列(受限磁盘IO,性能降低,不推荐)。
    注意:方案一二可以结合使用

四、RabbitMQ中死信交换机 ? (RabbitMQ延迟队列有了解过嘛)

DLX,全称为Dead-Letter-Exchange,可以称之为死信交换器,也有人称之为死信邮箱。当消息在一个队列中变成死信(dead message)之后,它能被重新被发送到另一个交换器中,这个交换器就是DLX,绑定DLX的队列就称之为死信队列。
延迟队列就是用到了死信交换机+TTL(消息存活时间)实现的。
场景:比如12306抢票,30分钟内顾客没有完成支付,就将该记录放入死信交换机,通过绑定的死信队列,进行座位的恢复工作。
实现: RabbitMQ可以使用延迟队列的插件实现,声明一个交换机,添加delayed属性为true,发送消息时候,添加x-delay头设置超时时间。

五、RabbitMQ的高可用机制有了解过嘛

我们当时项目在生产环境下,使用的集群,当时搭建是镜像模式集群,使用
了3台机器。
镜像队列结构是一主多从,所有操作都是主节点完成,然后同步给镜像节点,如果主节点宕机后,镜像节点会替代成新的主节点,不过在主从同步完成前,主节点就已经宕机,可能出现数据丢失。

那出现丢数据怎么解决呢?

在这里插入图片描述

我们可以采用仲裁队列,与镜像队列一样,都是主从模式,支持主从数据同步,主从同步基于Raft协议,强一致。并且使用起来也非常简单,不需要额外的配置,在声明队列的时候只要指定设置仲裁队列即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值