【rabbitMq】消息应答(三)

一、概念
消费者完成一个任务可能需要一段时间,若一个消费者处理一个长的任务,处理到一半它突然挂掉了,由于RabbitMq一旦想=向消费者传递了一条消息,便立即将该消息标记为删除。因此这种情况下消费者突然挂掉了,将会丢失正在处理的消息。以及后续发送给该消费者的消息,因为它无法接收到。
为了保证消息在发送过程中不丢失,rabbitmq引入了消息应答机制,消费者在接收到消息并且处理该消息之后。告诉rabbitmq它已经处理了,rabbitmq可以把该消息删除了
二、自定应答
消息发送后立即被认为已经传递成功,这种模式需要高吞吐量和数据传输安全性方面做权衡,因为在该模式下,在消息接收之前,消费者一侧出现链接或者channl关闭,那么消息就会丢失。
消息发送后立即被认为已经传送成功,这种模式需要在高吞吐量和数据传输安全性方面做权衡,因为这种模式如果消息在接收到之前,消费者那边出现连接或者 channel 关闭,那么消息就丢失了,当然另一方面这种模式消费者那边可以传递过载的消息,没有对传递的消息数量进行限制,当然这样有可能使得消费者这边由于接收太多还来不及处理的消息,导致这些消息的积压,最终使得内存耗尽,最终这些消费者线程被操作系统杀死,所以这种模式仅适用在消费者可以高效并以 某种速率能够处理这些消息的情况下使用。
三、消息应答的方法(手动应答方法)
1、Channel.basicAck(用于肯定确认),RabbitMq已经知道该消息并且成功被处理,可以将其丢弃了。
2、Channel.basicNack(用于否定确认,即不确定的意思)
3、Channel.basicReject(用于否定确认),与Channel.basicNack相比少一个参数,不处理该消息了直接拒接,可以将其丢弃。
Multiple解释:
手动应答的好处是可以批量应答并且减少网略拥堵
在这里插入图片描述
multiple的true和false代表不同意见:
true代表批量应答channel上未应答的消息。比如channel上有传送的tag消息5, 6,7, 8。当前tag是8,那么此时5-8的这些还未应答的消息都会被确认收到消息应答。
false代表只会应答tag=8的消息,5, 6,7这三个消息依然不会被确认收到消息应答。
在这里插入图片描述
四、消息自动入队
若消费者由于某些原因失去连接,导致消息未发送ACK确认,RabbitMq会了解到消息未完全处理,并将其重新排队。若此时其它消费者可以处理,它将会很快将消息发送给另一个消费者。此操作可以避免某个消费者偶尔挂掉,不丢失消息。
在这里插入图片描述
五、消息应答代码
在这里插入图片描述
1、生产者代码

package com.example.rabbitmq.three;

import com.example.rabbitmq.rabbitMqUtils.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;

// 消息在手动应答时不丢失,放回到队列中重新消费
public class Task2 {
    // 队列名称
    public static final String TASK_QUEUE_NAME = "ack_queue";

    // 发消息
    public static void main(String[] args) throws IOException, TimeoutException {
        // 获取信道
        Channel channel = RabbitUtils.getChannel();
        /*
         * 生成一个队列
         * 1、队列名称
         * 2、队列里面的消息是否持久化(磁盘)默认情况消息存储在内存中
         * 3、该队列是否共一个消费者进行消费,是否进行消息共享,true可以多个消费者消费,false只能一个消费者消费
         * 4、是否自动删除,最后一个消费者断开链接后,该队列是否自动删除,true自动删除,false不自动删除
         * 5、其它参数
         * */
        channel.queueDeclare(TASK_QUEUE_NAME, false, false, false, null);
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String message = scanner.next();
            /*
             * 发送一个消息
             * 1、发送到哪个交换机
             * 2、路由key是哪个,本次是队列的名称
             * 3、其他参数信息
             * 4、发送消息的消息体
             * */
            channel.basicPublish("", TASK_QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
            System.out.println("消息发送完成......");
        }
    }
}

2、消费者代码

package com.example.rabbitmq.three;

import com.example.rabbitmq.rabbitMqUtils.RabbitUtils;
import com.example.rabbitmq.rabbitMqUtils.SleepUtils;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Work03 {
    // 队列名称
    public static final String TASK_QUEUE_NAME = "ack_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = RabbitUtils.getChannel();
        System.out.println("C1等待接收消息处理时间较短");
        DeliverCallback deliverCallback = (consumerTag, message) -> {
        	// 沉睡1秒模拟消费者工作时间较短
            SleepUtils.sleep(1);
            System.out.println("接收到的消息:" + new String(message.getBody()));
            // 手动应答
            /**
             * 1、消息的标记 tag
             * 2、是否批量应答 false:不批量
             * */
            channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
        };

        // 取消消费的回调
        CancelCallback cancelCallback = (consumerTag) -> {
            System.out.println(consumerTag + "消息者取消消费回调逻辑");
        };
        // 采用手动应答
        boolean autoAck = false;
        // 采用手动应答
        channel.basicConsume(TASK_QUEUE_NAME, autoAck, deliverCallback, cancelCallback);
    }
}

package com.example.rabbitmq.three;

import com.example.rabbitmq.rabbitMqUtils.RabbitUtils;
import com.example.rabbitmq.rabbitMqUtils.SleepUtils;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Work04 {
    // 队列名称
    public static final String TASK_QUEUE_NAME = "ack_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = RabbitUtils.getChannel();
        System.out.println("C2等待接收消息处理时间较长");
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            // 沉睡30秒模拟消费者工作时间较长
            SleepUtils.sleep(30);
            System.out.println("接收到的消息:" + new String(message.getBody()));
            // 手动应答
            /**
             * 1、消息的标记 tag
             * 2、是否批量应答 false:不批量
             * */
            channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
        };

        // 取消消费的回调
        CancelCallback cancelCallback = (consumerTag) -> {
            System.out.println(consumerTag + "消息者取消消费回调逻辑");
        };
        // 采用手动应答
        boolean autoAck = false;
        // 采用手动应答
        channel.basicConsume(TASK_QUEUE_NAME, autoAck, deliverCallback, cancelCallback);
    }
}

启动生产者代码在控制台输入:aa,bb后将工作时间长的消费者关掉,此时bb消息会重新入队,被另一个正常的消费者消费,保证业务正常运行。
在这里插入图片描述

在这里插入图片描述

  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值