Rabbitmq手动应答代码实现与测试

本文介绍了RabbitMQ如何通过手动应答机制避免消息丢失。在自动应答模式下,消费者服务器宕机会导致消息丢失,但手动应答可以让消息回退队列并重新分配。通过生产者和消费者(消费者1号和消费者2号)的代码示例,展示了手动应答的实现过程。测试结果显示,当消费者2号宕机时,消息被1号消费者成功消费,验证了手动应答的有效性。
摘要由CSDN通过智能技术生成

前言

rabbitmq作为现在流行的消息队列,它拥有流量削峰、应用解耦、异步处理等优点,使用数量也是较多的。其中重要的特性也就是手动应答避免消息丢失的特点更是使其更上一层楼。消息队列基础的处理流程是:生产者—》队列—》消费者。

应答机制

rabbitmq的自动应答会导致在消息的分发途中,如果一台消费者角色的服务器宕机了,其处理的消息在自动应答模式下就会丢失,而手动应答则不会,而且退回队列分发给下一个消费者进行处理,有效的规避了因服务器宕机而造成的消息丢失。接下来进行代码演示

生产者

首先我们进行创建一个生产者进行消息的输入:

public class Task02 {

    private final static String TASK_QUEUE_NAME="queue01";

    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = RabbitMQUtils.getChannel();

        channel.queueDeclare(TASK_QUEUE_NAME,true,false,false,null);

        Scanner scanner = new Scanner(System.in);

        while (scanner.hasNext()){
            String message = scanner.next();
            channel.basicPublish("",TASK_QUEUE_NAME,null,message.getBytes("UTF-8"));
            System.out.println("生产者发出的消息是:"+message);

        }


    }
}

消费者

紧接着编写消费者的代码:
消费者1号

public class Work01 {

    private final static String TASK_QUEUE_NAME="queue01";

    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = RabbitMQUtils.getChannel();

        System.out.println("消费者1号等待接收消息处理时间较短!");

        DeliverCallback deliverCallback=(String consumerTag, Delivery message)->{
            //挂起一秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("接收到的消息是:"+new String(message.getBody(),"UTF-8"));

            //手动应答操作
            /**
             * 第一个参数:tag消息的标记
             * 第二个参数:是否批量处理,手动应答就为false
             */
            channel.basicAck(message.getEnvelope().getDeliveryTag(),false);

        };

        CancelCallback cancelCallback=(String consumerTag)->{
            System.out.println(consumerTag+"消费者消费信息被终止!");
        };
        //采用手动应答
        boolean autoAck=false;
        channel.basicConsume(TASK_QUEUE_NAME,autoAck,deliverCallback,cancelCallback);
    }
}

消费者2号:

public class Work02 {

    private final static String TASK_QUEUE_NAME="queue01";

    public static void main(String[] args) throws IOException, TimeoutException {

        Channel channel = RabbitMQUtils.getChannel();

        System.out.println("消费者2号等待接收消息处理时间较短!");

        DeliverCallback deliverCallback=(String consumerTag, Delivery message)->{
            //挂起一秒
            try {
                Thread.sleep(30000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("接收到的消息是:"+new String(message.getBody(),"UTF-8"));

            //手动应答操作
            /**
             * 第一个参数:tag消息的标记
             * 第二个参数:是否批量处理,手动应答就为false
             */
            channel.basicAck(message.getEnvelope().getDeliveryTag(),false);

        };

        CancelCallback cancelCallback=(String consumerTag)->{
            System.out.println(consumerTag+"消费者消费信息被终止!");
        };
        //采用手动应答
        boolean autoAck=false;
        channel.basicConsume(TASK_QUEUE_NAME,autoAck,deliverCallback,cancelCallback);
    }
}

测试

以上代码写完后我们依次点击运行,我们打开rabbitmq的web管理页面会发现刚刚创建得队列,这个队列就是一个消息队列,生产者所发送的消息首先会通过一个连接中的某个信道传输到队列中进行排列等待出队列,每个消息在出队列时又通过信道轮次传输到消费者进行消费。所以这个队列是很重要的,我们可以通过对其观察消息的输入输出的变化等相关信息。
在这里插入图片描述
接着我们发两条信息进行验证两个消费者是否可以正常进行消费:
在这里插入图片描述
然后检查消费者消费情况,观察是否正常消费:
在这里插入图片描述
在这里插入图片描述
1号消费者立即就收到了消息,2号因为设置了挂起30秒所以30后收到了消息,接着我们做一个测试,就是在2号消费者接收到消息处理时,关掉它造成服务器宕机,检测消息是否被1号消费者消费或是丢失。
在这里插入图片描述
在这里插入图片描述

结果

经过测试在2号服务器宕机之后,消息并未丢失而且重新回到队列轮次被1号消费者消费,这样以来就不会存在消息丢失问题,所以消息队列中一般多推荐使用手动应答防止消息丢失从而造成损失。

### RabbitMQ 手动应答实现方式 在RabbitMQ中,当消费者接收到并处理一条消息后,可以通过手动确认的方式告知服务器这条消息已被成功处理。这种方式可以确保即使消费者意外终止也不会丢失任何未处理的消息。 #### 配置监听容器以支持手动签收模式 要启用手动应答功能,在配置`SimpleMessageListenerContainer`时需设置其属性`acknowledgeMode`为`MANUAL`: ```java @Bean public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) { SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); container.setConnectionFactory(connectionFactory); container.setQueueNames("test.queue"); container.setMessageListener(listenerAdapter); // 设置为手动签收模式 container.setAcknowledgeMode(AcknowledgeMode.MANUAL); return container; } ``` #### 编写带有手动确认逻辑的消费者代码 下面是一个简单的Java方法来展示如何编写能够执行手动应答操作的服务端程序片段: ```java @Component public class Receiver { private static final Logger LOGGER = LoggerFactory.getLogger(Receiver.class); @RabbitListener(queues = "hello") public void receive(String in, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException { try{ System.out.println(" [x] Received '" + in + "'"); // 处理业务逻辑 // 成功处理完毕后发送基本确认命令给Broker channel.basicAck(tag, false); // 单条消息确认 // 或者使用 basicNack 来拒绝或重新排队消息 // channel.basicNack(tag, false, true/false); } catch (Exception e){ // 如果发生异常可以选择性的回滚事务或者让消息重回队列等待下次投递 channel.basicReject(tag, true); throw e; } } } ``` 在这个例子中,每当有新的消息到达指定队列时就会触发接收函数`receive()`。此函数会打印出所接受到的内容,并调用`basicAck()`来进行单个消息的手工确认;如果遇到错误,则通过`basicReject()`使消息返回至队首以便稍后再试[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

发飙的恒星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值