Bootstrap

15.RabbitMQ 消费端手动ACK消息确认机制

  ack——acknowledge(vt. 承认;答谢;报偿;告知已收到;确认的意思),在RabbitMQ中指代的是消费者收到消息后确认的一种行为,关注点在于消费者能否实际接收到MQ发送的消息。

  其提供了三种确认方式:

  自动确认acknowledge=“none”:当消费者接收到消息的时候,就会自动给到RabbitMQ一个回执,告诉MQ我已经收到消息了,不在乎消费者接收到消息之后业务处理的成功与否。

  手动确认acknowledge=“manual”:当消费者收到消息后,不会立刻告诉RabbitMQ已经收到消息了,而是等待业务处理成功后,通过调用代码的方式手动向MQ确认消息已经收到。当业务处理失败,就可以做一些重试机制,甚至让MQ重新向消费者发送消息都是可以的。

  根据异常情况确认acknowledge=“auto”:该方式是通过抛出异常的类型,来做响应的处理(如重发、确认等)这种方式比较麻烦。

  当消息一旦被消费者接收到,会立刻自动向MQ确认接收,并将响应的message从RabbitMQ消息缓存中移除,但是在实际的业务处理中,会出现消息收到了,但是业务处理出现异常的情况,在自动确认的模式下,该条业务处理失败的message就相当于被丢弃了。如果设置了手动确认,则需要在业务处理完成之后,手动调用channel.basicAck(),手动的签收,如果业务处理失败,则手动调用channel.basicNack()方法拒收,并让MQ重新发送该消息。

  如果不做任何关于acknowledge的配置,默认就是自动确认签收的。

  1、消费者配置:

server:
  port: 81

spring:
  rabbitmq:
    host: www.996.com
    port: 5672
    username: rabbitadmin
    password: rabbitadmin
    virtual-host: /
    listener:
      simple:
        acknowledge-mode: manual #手动

  2、消费者接口方法:

/**
 * 测试 ACK
 * @param message
 */
public void receiveMessage(String message);

  3、消费者接口方法实现:

/**
 * 测试 ACK
 * @param message
 */
@Override
@RabbitListener(queues = {RabbitMqConfig.DIRECT_QUEUE})
public void receiveMessage(String message) {
    System.out.println("接收到的MQ消息:"+message);
    //处理业务
    System.out.println("处理业务");
}

  4、生产者发送队列消息

/**
 * convertAndSend(String exchange, String routingKey, Object object),c
 *
 * @param message
 * @return
 */
@Override
public void sendMessage(String message) {
    CorrelationData correlationData = new CorrelationData("1");
    rabbitTemplate.convertAndSend(RabbitMqConfig.DIRECT_EXCHANGE,RabbitMqConfig.DIRECT_ROUTINGKEY,message,correlationData);
}

  5、队列
在这里插入图片描述
  6、消费者接收队列消息成功,但队列中的消息并未删除,只是状态变更为未确认状态
在这里插入图片描述
  7、更改消息接收方法,进行手动ACK确认

/**
 * 测试 ACK
 * @param message
 */
@Override
@RabbitListener(queues = {RabbitMqConfig.DIRECT_QUEUE})
public void receiveMessage(String message, Channel channel,@Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {

    try {
        System.out.println("接收到的MQ消息:"+message);
        //处理业务
        System.out.println("处理业务");
        //手动Ack
        /**
         * 手动Ack参数说明
         * basicAck(long deliveryTag, boolean multiple)
         * deliveryTag:批量处理的标号,举例:这个队列现在有5条消息要消费,那么这批数据会标号从1-5递增,5的时候就会手动Ack  multiple:是否批量处理
         *
         */
        System.out.println("deliveryTag:" + deliveryTag);
        channel.basicAck(deliveryTag,true);
    }catch (Exception e){
        e.getMessage();
    }

}

  8、消费者接收队列消息并手动ACK确认
在这里插入图片描述
  9、若业务中处理异常是可以拒签的,模拟业务

/**
 * 测试 ACK
 * @param message
 */
@Override
@RabbitListener(queues = {RabbitMqConfig.DIRECT_QUEUE})
public void receiveMessage(String message, Channel channel,@Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {

    try {
        System.out.println("接收到的MQ消息:"+message);
        //处理业务
        System.out.println("处理业务"+1/0);
        //手动Ack
        /**
         * 手动Ack参数说明
         * basicAck(long deliveryTag, boolean multiple)
         * deliveryTag:批量处理的标号,举例:这个队列现在有5条消息要消费,那么这批数据会标号从1-5递增,5的时候就会手动Ack  multiple:是否批量处理
         *
         */
        System.out.println("deliveryTag:" + deliveryTag);
        channel.basicAck(deliveryTag,true);
    }catch (Exception e){
        e.printStackTrace();
        /**
         * basicNack(long deliveryTag, boolean multiple, boolean requeue)
         * requeue:是否送回队列
         */
        try {
            channel.basicNack(deliveryTag,false,true);
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }

}

  10、测试,异常后消息会被送回队列再次被监听到继续消费,重试消费默认3次后便不再消费
在这里插入图片描述

;