RabbitMQ(七) 发布高级确认

122 阅读2分钟

介绍

在生产环境中由于一些不明原因,导致 rabbitmq重启,在RabbitMQ重启期间生产者消息投递失败,导致消息丢失,需要手动处理和恢复。于是,我们开始思考,如何才能进行RabbitMQ的消息可靠投递呢?特别是在这样比较极端的情况,RabbitMQ集群不可用的时候,无法投递的消息该如何处理呢:

1646883480(1).jpg

实现

  • 配置文件:添加
spring.rabbitmq.publisher-confirm-type = correlated

-配置类

// 配置类:发布确认(高级)
@Configuration
public class ConfirmConfig {

    //交换机
    public static final String CONFIRM_EXCHANGE_NAME = "confirm_exchange";
    //队列
    public static final String CONFIRM_QUEUE_NAME = "confirm_queue";
    //RoutingKey
    public static final String CONFIRM_routing_key = "key1";

    //声明交换机
    @Bean
    public DirectExchange confirmExchange(){
        return new DirectExchange(CONFIRM_EXCHANGE_NAME);
    }

    @Bean
    public Queue confirmQueue(){
        return QueueBuilder.durable(CONFIRM_QUEUE_NAME).build();
    }

    //绑定
    @Bean
    public Binding queueBindingExchange(@Qualifier("confirmQueue") Queue confirmQueue,
                                        @Qualifier("confirmExchange")DirectExchange confirmExchange){
        return BindingBuilder.bind(confirmQueue).to(confirmExchange).with(CONFIRM_routing_key);
    }
}
  • 生产者
// 开始发消息 测试确认
 @RestController
 @Slf4j
 @RequestMapping("/confirm")
 public class ProducerController {

 @Autowired
 private RabbitTemplate rabbitTemplate;

 //发消息
 @GetMapping("/sendMessage/{message}")
 public void sendMessage(@PathVariable String message){
     rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME
             ,ConfirmConfig.CONFIRM_routing_key
             ,message);
     log.info("发送消息内容:{}",message);
 }
}

-消费者

// 接收消息
 @Slf4j
 @Component
 public class Consumer {

 @RabbitListener(queues = ConfirmConfig.CONFIRM_QUEUE_NAME)
 public void receiveConfirmMessage(Message message){
     String msg = new String(message.getBody());
     log.info("接受到的队列confirm.queue消息:{}",msg);
 }
}

  • 回调接口
 @Component
 @Slf4j
 public class MyCallBack implements RabbitTemplate.ConfirmCallback {

 @Autowired
 private RabbitTemplate rabbitTemplate;

 @PostConstruct
 public void init(){
     //注入
     rabbitTemplate.setConfirmCallback(this);
 }

 /*
 * 交换机确认回调方法,发消息后,交换机接收到了就回调
 *   1.1 correlationData:保存回调消息的ID及相关信息
 *   1.2 b:交换机收到消息,为true
 *   1.3 s:失败原因,成功为null
 *
 * 发消息,交换机接受失败,也回调
 *   2.1 correlationData:保存回调消息的ID及相关信息
 *   2.2 b:交换机没收到消息,为false
 *   2.3 s:失败的原因
 *
  * */

 @Override
 public void confirm(CorrelationData correlationData, boolean b, String s) {
     String id = correlationData!=null ? correlationData.getId():"";
     if (b){
         log.info("交换机已经收到ID为:{}的信息",id);
     }else {
         log.info("交换机还未收到ID为:{}的消息,由于原因:{}",id,s);
     }
   }
 }

  • 配置文件及消息发送方

NONE:

禁用发布确认模式,是默认值

CORRELATED:

发布消息成功到交换器后会触发回调方法

SIMPLE:

经测试有两种效果,其一效果和CORRELATED值一样会触发回调方法,其二在发布消息成功后使用rabbitTemplate调用waitForConfirms,或 waitForConfirmsOrDie方法等待broker节点返回发送结果,根据返回结果来判定下一步的逻辑,要注意的点是waitForConfirmsOrDiea方法如果返回false则会关闭channel,则接下来无法发送消息到broker

spring.rabbitmq.publisher-confirm-type = correlated

  • 但此时出了问题,如果交换器出现问题可以回调接口,但是如果队列出问题无法会掉借口
  • Mandatory参数,见9.1