文章目录

1. 事务机制2. Confirm模式2.1 生产者2.1.1 普通Confirm模式2.1.2 批量Confirm模式2.1.3 异步Confirm模式2.2 消费者3. 其他

消费者如何确保消息一定能够消费成功呢?

由于在前面工作队列模式里面我们了解了应答模式,所以我们可以很自信的回答如上题目。

通过应答形式,默认自动应答,可以修改为手动应答来保证消息消费成功。

其实应答形式就是 RabbitMQ 消息确认机制的一种体现,我们再来看看问题的产生背景:

生产者发送消息出去之后,不知道到底有没有发送到 RabbitMQ 服务器, 默认是不知道的。而且有的时候我们在发送消息之后,后面的逻辑出问题了,我们不想要发送之前的消息了,需要撤回该怎么做。

两种解决方案:

  1. AMQP 事务机制
  2. Confirm 模式

1. 事务机制

事务机制分为三部分,开启事务,提交事务,事务回滚,如下:

  1. txSelect 将当前 channel 通道设置为 transaction 模式(开启事务)
  2. txCommit 提交当前事务
  3. txRollback 事务回滚

我们通过一个例子模拟消息生产者发送消息过程发生异常,进行事务回滚的过程。

public class Producer {

    /** 队列名称 */
    private static final String QUEUE_NAME = "test_queue";     public static void main(String[] args) throws IOException, TimeoutException {
        /** 1.获取连接 */
        Connection newConnection = MQConnectionUtils.newConnection();
         /** 2.创建通道 */
        Channel channel = newConnection.createChannel();
         /** 3.创建队列声明 */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        /** 4.发送消息 */
        try {             /** 4.1 开启事务 */
            channel.txSelect();
            String msg = "我是生产者生成的消息";
            System.out.println("生产者发送消息:"+msg);
            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
            /** 4.2 提交事务 - 模拟异常 */
            int i = 1/0;
            channel.txCommit();
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("发生异常,我要进行事务回滚了!");
            /** 4.3 事务回滚 */
            channel.txRollback();
        }finally {
            channel.close();
            newConnection.close();
        }     } }

打印结果:
生产者发送消息:我是生产者生成的消息
java.lang.ArithmeticException: / by zero at club.sscai.producer.Producer.main(Producer.java:37)
发生异常,我要进行事务回滚了!

2. Confirm模式

像上方这种采用 AMQP 事务机制来保证消息的准确到达,在一定程度上是消耗了性能的,所以我们再来看看 Confirm 模式。

Confirm 模式分为两块,一是生产者的 Confirm 模式,再就是消费者的 Confirm 模式。

2.1 生产者

通过生产者的确认模式我们是要保证消息准确达到客户端,而与 AMQP 事务不同的是 Confirm 是针对一条消息的,而事务是可以针对多条消息的。

Confirm 模式最大的好处在于它是异步的,一旦发布一条消息,生产者应用程序就可以在等信道返回确认的同时继续发送下一条消息,当消息最终得到确认之后,生产者应用便可以通过回调方法来处理该确认消息。

Confirm 的三种实现方式:

  1. channel.waitForConfirms() 普通发送方确认模式;
  2. channel.waitForConfirmsOrDie() 批量确认模式;
  3. channel.addConfirmListener() 异步监听发送方确认模式
2.1.1 普通Confirm模式
public class Producer11 {

    /** 队列名称 */
    private static final String QUEUE_NAME = "test_queue";     public static void main(String[] args) throws IOException, TimeoutException {
        /** 1.获取连接 */
        Connection newConnection = MQConnectionUtils.newConnection();
        /** 2.创建通道 */
        Channel channel = newConnection.createChannel();
        /** 3.创建队列声明 */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        /** 4.开启发送方确认模式 */
        channel.confirmSelect();
        /** 5.发送消息 */
        for (int i = 0; i < 5; i++) {
            channel.basicPublish("", QUEUE_NAME, MessageProperties.PERSISTENT_BASIC, (" Confirm模式, 第" + (i + 1) + "条消息").getBytes());
            try {
                if (channel.waitForConfirms()) {
                    System.out.println("发送成功");
                }else{
                    System.out.println("进行消息重发");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        /** 5.关闭通道、连接 */
        channel.close();
        newConnection.close();
    }
}

在推送消息之前,channel.confirmSelect() 声明开启发送方确认模式,再使用channel.waitForConfirms() 等待消息被服务器确认即可。

2.1.2 批量Confirm模式
public class Producer22 {

    /** 队列名称 */
    private static final String QUEUE_NAME = "test_queue";     public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        /** 1.获取连接 */
        Connection newConnection = MQConnectionUtils.newConnection();
        /** 2.创建通道 */
        Channel channel = newConnection.createChannel();
        /** 3.创建队列声明 */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        /** 4.开启发送方确认模式 */
        channel.confirmSelect();
        /** 5.发送消息 */
        for (int i = 0; i < 5; i++) {
            channel.basicPublish("", QUEUE_NAME, null, (" Confirm模式, 第" + (i + 1) + "条消息").getBytes());
        }
        /** 6.直到所有信息都发布,只要有一个未确认就会IOException */
        channel.waitForConfirmsOrDie();
        System.out.println("全部执行完成");
        /** 5.关闭通道、连接 */
        channel.close();
        newConnection.close();
    }
}

channel.waitForConfirmsOrDie() 使用同步方式等所有的消息发送之后才会执行后面代码,只要有一个消息未被确认就会抛出 IOException 异常。

2.1.3 异步Confirm模式
public class Producer33 {

    /** 队列名称 */
    private static final String QUEUE_NAME = "test_queue";     public static void main(String[] args) throws IOException, TimeoutException {
        /** 1.获取连接 */
        Connection newConnection = MQConnectionUtils.newConnection();
        /** 2.创建通道 */
        Channel channel = newConnection.createChannel();
        /** 3.创建队列声明 */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        /** 4.开启发送方确认模式 */
        channel.confirmSelect();
        for (int i = 0; i < 10; i++) {
            String message = "我是生产者生成的消息:" + i;
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
        }
        /** 5.发送消息 异步监听确认和未确认的消息 */
        channel.addConfirmListener(new ConfirmListener() {
            @Override
            public void handleNack(long deliveryTag, boolean multiple) throws IOException {
                System.out.println("未确认消息,标识:" + deliveryTag);
            }
            @Override
            public void handleAck(long deliveryTag, boolean multiple) throws IOException {
                System.out.println(String.format("已确认消息,标识:%d,多个消息:%b", deliveryTag, multiple));
            }
        });
        /** 6.关闭通道、连接 */
        /** channel.close();*/
        /** newConnection.close();*/
    } }

异步模式的优点,就是执行效率高,不需要等待消息执行完,只需要监听消息即可,以上异步返回的信息如下:

可以看出,代码是异步执行的,消息确认有可能是批量确认的,是否批量确认在于返回的 multiple 的参数,此参数为 bool 值,如果 true 表示批量执行了 deliveryTag 这个值以前的所有消息,如果为 false 的话表示单条确认。

维持异步调用要求我们不能断掉连接,因此注释掉第6步。

2.2 消费者

为了保证消息从队列可靠地到达消费者,RabbitMQ 提供消息确认机制(message acknowledgment)。消费者在声明队列时,可以指定 noAck 参数,当 noAck=false 时, RabbitMQ 会等待消费者显式发回ack信号后才从内存(和磁盘,如果是持久化消息的话)中移去消息。否则,RabbitMQ 会在队列中消息被消费后立即删除它。

在消费者中 Confirm 模式又分为手动确认和自动确认。

关于两者的介绍:

自动确认: 在自动确认模式下,消息在发送后立即被认为是发送成功。 这种模式可以提高吞吐量(只要消费者能够跟上),不过会降低投递和消费者处理的安全性。 这种模式通常被称为“发后即忘”。 与手动确认模式不同,如果消费者的TCP连接或信道在成功投递之前关闭,该消息则会丢失。

手动确认: 使用自动确认模式时需要考虑的另一件事是消费者过载。 手动确认模式通常与有限的信道预取一起使用,限制信道上未完成(“进行中”)传送的数量。 然而,对于自动确认,根据定义没有这样的限制。 因此,消费者可能会被交付速度所压倒,可能积压在内存中,堆积如山,或者被操作系统终止。 某些客户端库将应用TCP反压(直到未处理的交付积压下降超过一定的限制时才停止从套接字读取)。 因此,只建议当消费者可以有效且稳定地处理投递时才使用自动投递方式。

综上:尽量选择手动确认方式。

主要实现代码:

// 手动确认消息
channel.basicAck(envelope.getDeliveryTag(), false); // 关闭自动确认
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);

3. 其他

1、如果 RabbitMQ 服务器宕机了,消息会丢失吗?

不会丢失,RabbitMQ 服务器支持消息持久化机制,会把消息持久化到硬盘上。

2、如何确保消息正确地发送至RabbitMQ?

RabbitMQ 使用发送方确认模式,确保消息正确地发送到 RabbitMQ。

发送方确认模式:将信道设置成 confirm 模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的ID。一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一ID)。如果RabbitMQ发生内部错误从而导致消息丢失,会发送一条nack(not acknowledged,未确认)消息。

发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。


我创建了一个java相关的公众号,用来记录自己的学习之路,感兴趣的小伙伴可以关注一下微信公众号哈:niceyoo

RabbitMQ消息确认机制的更多相关文章

  1. RabbitMQ 消息确认机制

    消息确认机制 在之前异常处理部分就已经写了,对于consumer的异常退出导致消息丢失,可以时候consumer的消息确认机制.重复的就不说了,这里说一些不一样的. consumer的消息确认机制 当 ...

  2. RabbitMQ 消息确认机制以及lazy queue+ disk消息持久化

    一:Basic的一些属性,一些方法 1. 消费端的确认 自动确认: message出队列的时候就自动确认[broke] basicget... 手工确认: message出队列之后,要应用程序自己去确 ...

  3. SpringBoot(九)RabbitMQ安装及配置和使用,消息确认机制

    Windows下RabbitMQ安装及配置地址: https://blog.csdn.net/zhm3023/article/details/82217222RabbitMQ(四)订阅模式:https ...

  4. springboot + rabbitmq 用了消息确认机制,感觉掉坑里了

    本文收录在个人博客:www.chengxy-nds.top,技术资源共享,一起进步 最近部门号召大伙多组织一些技术分享会,说是要活跃公司的技术氛围,但早就看穿一切的我知道,这 T M 就是为了刷KPI ...

  5. RabbitMQ消息确认(发送确认,接收确认)

    前面几篇记录了收发消息的demo,今天记录下关于 消息确认方面的 问题. 下面是几个问题: 1.为什么要进行消息确认? 2.rabbitmq消息确认 机制是什么样的? 3.发送方如何确认消息发送成功? ...

  6. (转)RabbitMQ消息队列(九):Publisher的消息确认机制

    在前面的文章中提到了queue和consumer之间的消息确认机制:通过设置ack.那么Publisher能不到知道他post的Message有没有到达queue,甚至更近一步,是否被某个Consum ...

  7. RabbitMQ消息队列(九):Publisher的消息确认机制

    在前面的文章中提到了queue和consumer之间的消息确认机制:通过设置ack.那么Publisher能不到知道他post的Message有没有到达queue,甚至更近一步,是否被某个Consum ...

  8. RabbitMQ (十二) 消息确认机制 - 发布者确认

    消费者确认解决的问题是确认消息是否被消费者"成功消费". 它有个前提条件,那就是生产者发布的消息已经"成功"发送出去了. 因此还需要一个机制来告诉生产者,你发送 ...

  9. RabbitMQ学习笔记之五种模式及消息确认机制

    本文详细介绍简单模式Simple.工作模式Work.发布订阅模式Publish/Subscribe.Topic.Routing. Maven依赖引用 <dependencies> < ...

随机推荐

  1. Visual Studio 2019(VS2019)下载极慢的问题

    今天给新电脑安装 vs2019,下载速度只有 TMD 10kb/s,实在忍无可忍,在经过了一系列的百度之后,找到解决方法. 具体方法很简单: 用站长工具查询了一下 download.visualstu ...

  2. Linux 启动数据库报错:could not open parameter file init**.ora

    sqlplus /nolog.conn /as sysdba.startup命令后显示 SQL> startupORA-01078: failure in processing system p ...

  3. FFT(快速傅里叶变换)

    FFT(快速傅里叶变换) 前置知识 \(1.复数\) \(2.单位根\) \(3.循环结构\) \(4.C++\) 1.复数 \(定义:形如a+bi的数,其中i^2=-1\) \(计算:1.(a+bi ...

  4. Aliplayer视频点播加密播放

    Aliplayer视频播放器填坑 aliplayer视频点播分为Flash和HTML5两个版本,移动端不支持Flash播放器.Flash播放器兼容IE8+,HTML5播发器支持比较新的浏览器,对浏览器 ...

  5. c++小学期大作业攻略(一)环境配置

    UPDATE at 2019/07/20 20:21 更新了Qt连接mysql的方法,但是是自己仿照连VS的方法摸索出来的,简单测试了一下能work但是不保证后期不会出问题.如果你在尝试过程中出现了任 ...

  6. [转帖]springboot+k8s+抛弃springcloud.eureka

    springboot+k8s+抛弃springcloud.eureka https://www.cnblogs.com/lori/p/12048743.html springboot开发微服务框架一般 ...

  7. Mysql load data infile 命令导入含中文csv源数据文件 【错误代码 1300】

    [1]Load data infile 命令导入含中文csv源数据文件 报错:Invalid utf8 character string: '??֧' (1)问题现象 csv格式文件源数据: 导入SQ ...

  8. java中的对象、类、包、模块、组件、容器、框架、架构的概念入门

    在Java中有那么一些概念:对象.类.包.模块.组件.容器.框架.这些概念都有一个共同的特点,就是[容纳]. 对象(Object) 在Java的世界里,对象是通过属性和方法来分别对应事务所具有的静态属 ...

  9. Prometheus 安装Alertmanager集成

    Prometheus 安装Alertmanager集成 # 下载地址 地址1:https://prometheus.io/download/ 地址2:https://github.com/promet ...

  10. [個人紀錄] regular 搜集

    判斷有理數 ^(0|[1-9]([0-9]{1,5})?)((\.(([0-9]{1,5})?[1-9])))?$