RabbitMq学习(六)消息确认之接收确认

为什么需要接收确认

RabbitMQ默认会在消息被消费者接收后,立即确认。但存在丢失消息的可能,如果消费端消费逻辑抛出异常,也就是消费端没有处理成功这条消息,那么就相当于丢失了消息。
另外一种情况就是,我们在spring中处理消息时,即使消息处理没出异常,但是后续代码出异常造成回滚,这样其实也相当于丢失消息。
所以一般情况下,手动确认要比较好一些。

消息确认模式

AcknowledgeMode.NONE:自动确认
AcknowledgeMode.AUTO:根据情况确认
AcknowledgeMode.MANUAL:手动确认

开启消息手动确认

配置文件中:

spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: manual

配置类:

@Bean
public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
	SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
	factory.setConnectionFactory(connectionFactory);
	//使用jackson进行消息序列与反序列
	factory.setMessageConverter(new Jackson2JsonMessageConverter());
	factory.setAcknowledgeMode(AcknowledgeMode.MANUAL); // 开启手动 ack
	return factory;
}

确认消息的API

通过com.rabbitmq.client.Channel中的方法来进行消息确认

//确认消费成功
void basicAck(long deliveryTag, boolean multiple) throws IOException;
//确认消费失败
void basicNack(long deliveryTag, boolean multiple, boolean requeue)
            throws IOException;
//拒绝消息        
void basicReject(long deliveryTag, boolean requeue) throws IOException;

关于参数:
deliveryTag:RabbitMQ 向该 Channel 投递的这条消息的唯一标

multiple:是否批量处理,当该参数为 true 时,则可以一次性确认 delivery_tag 小于等于传入值的所有消息
requeue:是否重新放入队列

消息消费过程中异常处理

大致有以下三种处理方式

  1. 内部catch后直接处理,然后使用channel对消息进行确认。
    这种需要设置手动处理,然后用channel进行消息确认。这种操作方式,若是消费出现异常,调用basicNack方法时的参数requeue若是true,消息就会一直被重新放入队列,然后消费,然后异常,死循环。
    处理方式:
    为了避免消息处理异常造成死循环,我们可以将requeue设置成fasle,这时消息会进入“死信”队列。然后我们可以监听死信队列来做异常处理。
    我们可以在设置队列时指定死信队列的交换机和路由key:

    //声明队列,并给队列增加x-dead-letter-exchange和x-dead-letter-routing-key参数,用于指定死信队列的路由和routingKey
    @Bean
    public Queue queue(){
        Map<String, Object> args = new HashMap<String, Object>();
        args.put("x-dead-letter-exchange",IntegralConstant.DEAD_EXCHANGE_NAME);
        args.put("x-dead-letter-routing-key",IntegralConstant.DEAD_ROUTING_KEY);
        return new Queue(IntegralConstant.QUEUE_NAME, true, false, false, args);
    }
    

    然后我们在进行消息确认时就可以做如下操作:

    ```
    /消息确认时使用nack,并且requeue参数传false
    channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
    ```
    

    顺便提一下消息变成死信的情况:
    消息被拒绝(basic.reject/ basic.nack)并且requeue=false
    消息TTL过期(参考:RabbitMQ之TTL(Time-To-Live 过期时间))
    队列达到最大长度

  2. 配置RepublishMessageRecoverer将处理异常的消息发送到指定队列专门处理或记录
    注意,这是在消息方法中不处理异常的情况下,spring.rabbitmq.listener.retry配置的重试次数用完以后还是抛异常的话才会调用这个RepublishMessageRecoverer来处理

    @Bean
    public MessageRecoverer messageRecoverer(RabbitTemplate rabbitTemplate){
        return new RepublishMessageRecoverer(rabbitTemplate, "errorExchange", "errorRoutingKey");
    }
    
  3. ErrorHandler
    利用@RabbitListener中的属性errorHandler实现一个异常监听处理器:

    /**
    	 * 
    	 * @return
    	 */
    	@Bean
    	public RabbitListenerErrorHandler rabbitListenerErrorHandler() {
    		return (amqpMessage, message, exception) -> {
    			System.out.println("进入handler");
    			System.out.println(new String(amqpMessage.getBody()));
    			return null;
    		};
    	}
    

总结

  1. 若是开启的自动确认,可以使用第二种和第三种方法进行消费异常的处理。
  2. 若是开启的手动确认,监听的方法内部必须使用channel进行消息确认,包括消费成功或消费失败,使用第一种方法即可。
  3. 手动确认情况下,推荐在消息消费失败时,将消息放入死信队列(requeue=false)

猜你喜欢

转载自blog.csdn.net/qq_38846242/article/details/84958640