RabbitMQ高级特性——超详细说明Consumer Ack(消费者端消息确认,附代码)

大伙可以到我的RabbitMQ专栏获取更多信息

demo示例这里拿

概述

上一篇文章RabbitMQ高级特性——消息的可靠投递(confirm模式与return模式)中介绍了RabbitMQ提供的两种保证消息投递可靠性的方式:

  • confirm确认模式
  • return 退回模式

这两种方式实质上是提供了对消息生产者侧的消息可靠性保证,保证了生产者的消息能正确的进入预期的队列中。本文将介绍一下Consumer Ack,这是一种面向消费者的消息可靠性保证方案。


Consumer 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的配置,默认就是自动确认签收的

代码示例

生产者端没有变化,能发消息就可以

    /*
     * 功能描述: <br>
     * 〈测试Consumer Ack〉
     * @Param: []
     * @Return: void
     * @Author: LeoLee
     * @Date: 2020/11/7 21:12
     */
    @Test
    public void testAck() {

//        //消费者接收到该消息,解析到true,就模拟调用channel.basicAck确认签收消息
//        rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME, "boot.test", "test msg send [true]");

        //消费者接收到该消息,解析到false,就模拟调用channel.basicNack,拒收消息,让MQ重发
        rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME, "boot.test", "test msg send [false]");
    }

消费者端打开Ack模式

server:
  port: 2002

spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: xxxxxxxxx
    password: xxxxxxxxx
    virtual-host: /LeoLee
    listener:
      simple:
        acknowledge-mode: manual #消费者端确认模式:none自动确认 manual手动确认 auto通过抛出异常的类型,来做响应的处理
        concurrency: 1 #当前监听的数量
        max-concurrency: 5 #最大监听数量
        retry:
          enabled: true #是否支持重试
          max-attempts: 4 #最大重试次数,默认为3

消费者端创建一个listener并实现ChannelAwareMessageListener接口(其实也可以不实现该接口,只要@RabbitListener标记的方法,或者@RabbitListener标记的类+@RabbitHandler标记的方法的参数列表有[com.rabbitmq.client.Channel]和[org.springframework.amqp.core.Message]两个参数,都可以

package com.leolee.rabbitmq.MsgListener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @ClassName AckListener
 * @Description: Consumer Ack
 * 1.设置手动确认签收:acknowledge-mode: manual, retry.enabled: true #是否支持重试
 * 2.实现ChannelAwareMessageListener接口,ChannelAwareMessageListener是MessageListener的子接口
 * 3.如果消息接收并处理完成,调用channel.basicAck()向MQ确认签收
 * 4.如果消息接收但是业务处理失败,调用channel.basicNack()拒收,要求MQ重新发送
 * @Author LeoLee
 * @Date 2020/11/7
 * @Version V1.0
 **/
@Component
public class AckListener implements ChannelAwareMessageListener {


    @RabbitListener(queues = "boot_queue")
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {

        Thread.sleep(1000);
        boolean tag = new String(message.getBody()).contains("true");
        System.out.println("接收到msg:" + new String(message.getBody()));
        //获取mes deliveryTag
        long deliveryTag = message.getMessageProperties().getDeliveryTag();

        try {
            if (tag) {
                System.out.println("业务处理成功");
                //手动签收
                /*
                 * deliveryTag:the tag from the received {@link com.rabbitmq.client.AMQP.Basic.GetOk} or {@link com.rabbitmq.client.AMQP.Basic.Deliver}
                 * multiple: ture确认本条消息以及之前没有确认的消息,false仅确认本条消息
                 */
                channel.basicAck(deliveryTag, false);
            } else {
                //模拟业务处理失败抛出异常
                System.out.println("业务处理失败");
                throw new IOException("业务处理失败");
            }
        } catch (IOException e) {
            e.printStackTrace();
            /*
             * deliveryTag:the tag from the received {@link com.rabbitmq.client.AMQP.Basic.GetOk} or {@link com.rabbitmq.client.AMQP.Basic.Deliver}
             * multiple: ture确认本条消息以及之前没有确认的消息,false仅确认本条消息
             * requeue: true该条消息重新返回MQ queue,MQ broker将会重新发送该条消息
             */
            channel.basicNack(deliveryTag, false, true);
            //也可以使用channel.basicReject(deliveryTag, requeue),它只能拒收单条消息
            //channel.basicReject(deliveryTag, true);
        }

    }
}

用生产者测试类发送不同的消息给MQ

成功接收并手动确认后,MQ队列就删除了该消息的缓存:

被拒绝的消息会一直发送:

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java中使用RabbitMQ消费者监听队列并实现消息确认机制的代码示例如下: ```java import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.DeliverCallback; public class RabbitMQConsumer { private final static String QUEUE_NAME = "myQueue"; public static void main(String[] args) throws Exception { // 创建连接工厂 ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); // RabbitMQ服务器地址 // 创建连接 Connection connection = factory.newConnection(); // 创建信道 Channel channel = connection.createChannel(); // 声明队列 channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 定义消息接收回调函数 DeliverCallback deliverCallback = (consumerTag, delivery) -> { String message = new String(delivery.getBody(), "UTF-8"); System.out.println("Received message: " + message); // 手动确认消息已被消费 channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); }; // 监听队列并消费消息 channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> {}); // 等待消息处理完成后关闭连接 Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { channel.close(); connection.close(); } catch (Exception e) { e.printStackTrace(); } })); } } ``` 这段代码中,首先创建了一个连接工厂(ConnectionFactory),设置RabbitMQ服务器地址,然后创建连接(Connection)和信道(Channel)。接下来,声明了要监听的队列(QUEUE_NAME)。然后,定义了消息接收的回调函数(DeliverCallback),在回调函数中处理接收到的消息,并通过`channel.basicAck`手动确认消息已被消费。最后,通过`channel.basicConsume`方法启动监听队列并消费消息的过程。 请注意,以上代码仅提供了基本的示例,实际应用中还需要考虑异常处理、连接关闭等情况。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值