四、RabbitMQ之消息确认

 application.yml的配置

spring:
  application:
    name: rabbitmq-provider
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    publisher-confirms: true #消息发送到Broker,是否确认
    publisher-returns: true #消息未送达队列,是否回调
    listener:
      simple:
        acknowledge-mode: manual #消费确认模式,手动MANUAL,自动AUTO,无NONE
logging:
  level:
    org.springframework.amqp.rabbit.core.RabbitTemplate: debug
    org.springframework.amqp.rabbit.AsyncRabbitTemplate: debug

确认部分日志代码:

public class AckAsyncRabbitTemplate extends AsyncRabbitTemplate {

    public AckAsyncRabbitTemplate(RabbitTemplate template, AbstractMessageListenerContainer container) {
        super(template, container);
    }

    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        log.info("消息主体:{},应答码:{},应答信息:{},交换机:{},路由键:{}", new String(message.getBody()), replyCode, replyText, exchange, routingKey);
    }

    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (ack) {
            log.info("消息发送成功:{}", correlationData);
        } else {
            log.info("消息发送失败:{}", cause);
        }
    }
}

 

1.消息发送确认

  • 消息由生产者->exchange的确认:

发送成功时 

消息发送成功:CorrelationData [id=5799031a-a0df-49cb-aadd-af0db28edbd8]

当页面删除exchange之后,消息发送失败

消息发送失败:channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'exchangeAck' in vhost '/', class-id=60, method-id=40)
  • 消息由exchange->queue的确认:当消息没有成功进入队列时,会返回消息,使用时都会将队列和交换机绑定,这个确认一般不会用到,其他情况暂时没有考虑到

 

消息主体:avcs,应答码:312,应答信息:NO_ROUTE,交换机:exchangeAck,路由键:A

 

2.消息消费确认

@Slf4j
public class AckConsumer {
    int index = 1;

    @RabbitListener(queues = QUEUE_NAME)
    public void consume(Message message, Channel channel) {
        String s = new String(message.getBody());
        log.info("msg-{}", s);
        try {
            if ("ack".equals(s)) {
                //手动确认消息已消费,可以直接删除
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            } else if ("requeue".equals(s) && index < 5) {
                try {
                    Thread.sleep(3000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //重新入队列,测试结果看到后面发送的消息会被先消费,所以应该是排到队列尾部的
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
                index++;
            } else {
                //不重新入队列,直接删除
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
                index = 0;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 3.确保消息顺序消费 - 仅代表个人理解

 一个队列中的消息是顺序被消费的,那么我们只需保证是同一个消费者即可

具体做法:比如同一批业务数据的处理,加一个唯一的线索mqID,当mqID一致时,都发送到某一个队列中去

PS:如果开启了手动确认机制,在消费消息1时失败(如消费过程中消费者宕机,rabbit服务端检测到链接断开,会自动将消息重新入队列,放到队列的后面去),那此时消息2被消息也会出问题,这种情况考虑在业务上做处理(如状态不符合),依次将这批消息重新入队列,重新从头开始消费

 

posted on 2020-09-18 15:20  Hleaves  阅读(261)  评论(0编辑  收藏  举报