RabbitMQ如何保证不丢数据? Spring Boot实现confirm机制及ack消费端主动回调

948 阅读1分钟
  1. yml配置:

    spring:
      rabbitmq:
        host: 127.0.0.1
        port: 5672
        username: guest
        password: guest
        #发送确认 对应RabbitTemplate.ConfirmCallback接口
        publisher-confirms: true
        #发送失败回退,对应RabbitTemplate.ReturnCallback接口
        publisher-returns: true
        #手动提交ack
        listener:
          direct:
            acknowledge-mode: manual
          simple:
            acknowledge-mode: manual


  2. config配置:
    @Configuration
    public class RabbitConfig {
     
        /**
         * 定义一个队列
         * @return
         */
        @Bean
        public Queue okongQueue() {
            return new Queue("user");
        }
     
        @Bean
        MessageConverter messageConverter() {
            return new Jackson2JsonMessageConverter();
        }
     
    }
  3. 发送端:
    @Service
    public class SenderService implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback {
     
        @Autowired
        private RabbitTemplate rabbitTemplate;
     
        public void sendMq() throws InterruptedException {
            this.rabbitTemplate.setConfirmCallback(this);
            rabbitTemplate.convertAndSend("user", "6666666");
        }
     
        @Override
        public void confirm(CorrelationData correlationData, boolean ack, String cause) {
            System.out.println("=====已消费======");
            if(ack){
                System.out.println("消息: "+correlationData+",已经被ack成功");
            }else{
                System.out.println("消息: "+correlationData+",nack,失败原因是:"+cause);
            }
        }
     
        @Override
        public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
            System.out.println("sender return success" + message.toString());
        }
    }
  4. 消费端
    @Component
    @RabbitListener(queues = "user")
    @Slf4j
    public class Consumer {
     
        @RabbitHandler
        public void process(String messageStr, Channel channel, Message message) {
            log.info("接收的消息为: {}", messageStr);
            try {
                //通知服务器此消息已经被消费,可从队列删掉, 这样以后就不会重发,否则后续还会在发
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    还可指定以下处理方式,可参考api:

            //消息的标识,false只确认当前一个消息收到,true确认所有consumer获得的消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
     
            //ack返回false,并重新回到队列,api里面解释得很清楚
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
     
            //丢弃这条消息
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
     
            //拒绝消息
            channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);


  5. 指定控制层调用:
    @RestController
    public class RabbitMqController {
     
        @Autowired
        private SenderService senderService;
     
        @PostMapping("/sendMq")
        public void sendMq() throws InterruptedException {
            senderService.sendMq();
        }
     
    }
  6. 至此,已成功实现,亲测可用!