文章目录

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. 奥展项目笔记02--一个bat文件运行多个java jar包

    奥展项目中后端微服务有很多jar包,一个一个启动又费时间效率又低,怎么才能一下让所有的jar包一块运行呢?我们可以编写.bat文件来一键启动. 1.我们将.bat文件放到jar包的同一级目录文件夹中: ...

  2. Docker 部署ELK之Sentinl日志报警

    前篇文章简单介绍了Docker 部署ELK,以及使用filebeat收集java日志.这篇我们介绍下日志报警配置,这里我们使用Sentinl插件. 1.修改kibana参数 进入elk容器,修改对应参 ...

  3. Turbo码基本框架

    1. 基本原理 1.1 定义 1.2 分量码设计 2. 编码方法 2.1 交织器的设计 2.2 分量编码器 2.3 删余矩阵及复用 3. 译码方法和算法 3.1 迭代译码 3.2 MAP类算法 3.3 ...

  4. xampp windows10下xdebug调试环境安装及配置

    xampp是在windows环境下做php,mysql开发的全家桶,免去了很多apache, php集成配置,数据库驱动安装配置的过程,应用非常广泛. xdebug是php开发调试必备利器,本文就记录 ...

  5. Python关于多继承

    大部分面向对象的编程语言(除了C++)都只支持单继承,而不支持多继承,为什么呢?因为多继承不仅增加编程复杂度,而且容易导致莫名其妙的错误. Python虽然语法上支持多继承,但是却不推荐使用多继承,而 ...

  6. python 函数式编程 闭包,返回一个函数

    参考链接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017434209254976 作业 #使用生成器 def createCounter( ...

  7. 字节输出流FileOutputStream

    #字节流 字节输出流FileOutputStream 创建输出流对象 OutputStream 流对象是一个抽象类,不能实例化.所以,我们要找一个具体的子类 :FileOutputStream. 查看 ...

  8. C++中的Point类与vector类的简单处理

    首先设计Vector与Point的表示方法,再依次完善两个类中的构造函数,复制构造函数等. 向量由两个点表示,当进行运算的时候,转化起点坐标为(0,0): 第14行:由于Vector需要用到Point ...

  9. Linux组管理(6)

    在linux中每个用户必须属于一个组,不能独立于组外.在linux中每个文件有所有者.所在组.其它组的概念. 文件/目录的所有者:一般为文件的创建者,谁创建了该文件,就自然成为该文件的所有者 查看文件 ...

  10. 解决:ERROR 1067 (42000): Invalid default value for 'login_time'

    如图操作数据表的时候出现上图的错误 问题的原因就是出在timestamp的默认值不正确,针对以上问题的解决方案是:修改默认值为当前值. sql语言代码: Alter table user modify ...