首页 > 其他分享 >【Rabbitmq篇】RabbitMQ⾼级特性----消息确认

【Rabbitmq篇】RabbitMQ⾼级特性----消息确认

时间:2024-11-17 17:48:17浏览次数:3  
标签:确认 System RabbitMQ ---- Rabbitmq message public 消息

目录

前言:

一.消息确认机制 

• ⾃动确认

• ⼿动确认

 手动确认方法又分为三种:

二. 代码实现(spring环境)

配置相关信息:

1). AcknowledgeMode.NONE 

2 )AcknowledgeMode.AUTO

3)AcknowledgeMode.MANUAL

总结:


前言:

前期讲了RabbitMQ的概念和应⽤,RabbitMQ实现了AMQP0-9-1规范的许多扩展,在RabbitMQ官⽹上,也给⼤家介绍了RabbitMQ的⼀些特性,我们挑⼀些重要的且常⽤的给⼤家讲⼀下

Rabbitmq官网


一.消息确认机制 

⽣产者发送消息之后,到达消费端之后,可能会有以下情况:
a. 消息处理成功
b. 消息处理异常

RabbitMQ向消费者发送消息之后,就会把这条消息删掉,那么第两种情况,就会造成消息丢失.
那么如何确保消费端已经成功接收了,并正确处理了呢
为了保证消息从队列可靠地到达消费者,RabbitMQ提供了消息确认机制(messageacknowledgement)。

消费者在订阅队列时,可以指定autoAck参数,根据这个参数设置,消息确认机制分为以下两种: 

• ⾃动确认

当autoAck等于true时, RabbitMQ 会⾃动把发送出去的消息置为确认,然后从内存(或者磁盘)中删除,⽽不管消费者是否真正地消费到了这些消息.⾃动确认模式适合对于消息可靠性要求不⾼的场景.


• ⼿动确认

当autoAck等于false时,RabbitMQ会等待消费者显式地调⽤Basic.Ack命令,回复确认信号后才从内存(或者磁盘)中移去消息.这种模式适合对消息可靠性要求⽐较⾼的场景. 

 手动确认方法又分为三种:

  • 肯定确认:Channel.basicAck(long deliveryTag, boolean multiple)                  RabbitMQ已知道该消息并且成功的处理消息,可以将其丢弃了.
  • 否定确认: Channel.basicReject(long deliveryTag, boolean requeue) 
    消费者客⼾端可以调⽤channel.basicReject⽅法来告诉RabbitMQ拒绝这个消息.
  • 否定批量确认: Channel.basicNack(long deliveryTag, boolean multiple,boolean requeue)
    Basic.Reject命令⼀次只能拒绝⼀条消息,如果想要批量拒绝消息,则可以使⽤Basic.Nack这个命令.消费者客⼾端可以调⽤channel.basicNack⽅法来实现.

参数说明:

1)deliveryTag :
消息的唯⼀标识,它是⼀个单调递增的64位的⻓整型值. deliveryTag 是每个通道
(Channel)独⽴维护的,所以在每个通道上都是唯⼀的.当消费者确认(ack)⼀条消息时,必须使⽤对应的通道上进⾏确认.

2)multiple 

是否批量确认.在某些情况下,为了减少⽹络流量,可以对⼀系列连续的 deliveryTag 进
⾏批量确认.值为true则会⼀次性ack所有⼩于或等于指定deliveryTag的消息.值为false,则只确认当前指定deliveryTag的消息.

 

 3)requeue

表⽰拒绝后,这条消息如何处理.如果requeue参数设置为true,则RabbitMQ会重新将这条
消息存⼊队列,以便可以发送给下⼀个订阅的消费者.如果requeue参数设置为false,则RabbitMQ会把消息从队列中移除,⽽不会把它发送给新的消费者.


二. 代码实现(spring环境)

1.可以直接使用RabbitMQ Java Client 库

2.使用spring集成的amqp

 主要介绍第二种,在spring环境下实现

Spring-AMQP 对消息确认机制提供了三种策略.

public enum AcknowledgeMode { 
    NONE //确认,
    MANUAL//手动 ,
    AUTO //默认;
}

配置相关信息:

基本信息以及确认机制

队列,交换机,以及它们之间的绑定关系 

package com.bite.extensions.config;

import com.bite.extensions.constant.Constants;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitConfig {
    @Bean("ackQueue")
    public Queue ackQueue() {
        return QueueBuilder.durable(Constants.ACK_QUEUE).build();
    }
    @Bean("directExchange")
    public DirectExchange directExchange() {
        return ExchangeBuilder.directExchange(Constants.ACK_EXCHANGE).build();
    }
    @Bean("ackBinding")
    public Binding ackBinding(@Qualifier("directExchange") DirectExchange directExchange,@Qualifier("ackQueue") Queue queue) {
        return BindingBuilder.bind(queue).to(directExchange).with("ack");
    }
}

生产者:

主要解释消费者在不同确认机制的状态

package com.bite.extensions.controller;

import com.bite.extensions.constant.Constants;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/producer")
@RestController
public class ProducerController {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @RequestMapping("/ack")
    public String ack() {
        rabbitTemplate.convertAndSend(Constants.ACK_EXCHANGE,"ack","consumer ack mode test...");
        return "消息发送成功!";
    }
}

1)AcknowledgeMode.NONE 

这种模式下,消息⼀旦投递给消费者,不管消费者是否成功处理了消息,RabbitMQ就会⾃动确认
消息,从RabbitMQ队列中移除消息.如果消费者处理消息失败,消息可能会丢失.

 

1)消费者 正常消费情况下

package com.bite.extensions.listener;

import com.bite.extensions.constant.Constants;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;

@Component
public class AckListener {
    @RabbitListener(queues = Constants.ACK_QUEUE)
    public void handleMessage(Message message, Channel channel) throws UnsupportedEncodingException {
        //消费者逻辑
        long deliverTag = message.getMessageProperties().getDeliveryTag();
        System.out.printf("接收到信息: %s, deliveryTag: %d\n",new String(message.getBody(),"UTF-8"),deliverTag);
        //业务逻辑处理
        System.out.println("业务逻辑处理!");

        System.out.println("业务逻辑完成!");
    }
}

消费者正确处理,MQ删除相应信息

2)消费者 异常消费情况下

@Component
public class AckListener {
    @RabbitListener(queues = Constants.ACK_QUEUE)
    public void handleMessage(Message message, Channel channel) throws UnsupportedEncodingException {
        //消费者逻辑
        long deliverTag = message.getMessageProperties().getDeliveryTag();
        System.out.printf("接收到信息: %s, deliveryTag: %d\n",new String(message.getBody(),"UTF-8"),deliverTag);
        //业务逻辑处理
        System.out.println("业务逻辑处理!");
        int num = 3/0; //异常
        System.out.println("业务逻辑完成!");
    }
}

 可以看到,消费者处理失败,但是消息已经从RabbitMQ中移除.

2 )AcknowledgeMode.AUTO

这种模式下,消费者在消息处理成功时会⾃动确认消息,但如果处理过程中抛出了异常,则不会确认消息. 

    listener:
      simple:
        acknowledge-mode: auto  #消息接收确认

1)消费者 正常消费情况下 

@Component
public class AckListener {
    @RabbitListener(queues = Constants.ACK_QUEUE)
    public void handleMessage(Message message, Channel channel) throws UnsupportedEncodingException {
        //消费者逻辑
        long deliverTag = message.getMessageProperties().getDeliveryTag();
        System.out.printf("接收到信息: %s, deliveryTag: %d\n",new String(message.getBody(),"UTF-8"),deliverTag);
        //业务逻辑处理
        System.out.println("业务逻辑处理!");
        //int num = 3/0;
        System.out.println("业务逻辑完成!");
    }
}

 

2)消费者 异常消费情况下 

@Component
public class AckListener {
    @RabbitListener(queues = Constants.ACK_QUEUE)
    public void handleMessage(Message message, Channel channel) throws UnsupportedEncodingException {
        //消费者逻辑
        long deliverTag = message.getMessageProperties().getDeliveryTag();
        System.out.printf("接收到信息: %s, deliveryTag: %d\n",new String(message.getBody(),"UTF-8"),deliverTag);
        //业务逻辑处理
        System.out.println("业务逻辑处理!");
        int num = 3/0;
        System.out.println("业务逻辑完成!");
    }
}
..........
接收到信息: consumer ack mode test..., deliveryTag: 88
业务逻辑处理!
2024-11-17T15:19:11.420+08:00  WARN 22936 --- [rabbitmq-extensions-demo] [ntContainer#0-1] s.a.r.l.ConditionalRejectingErrorHandler : Execution of Rabbit message listener failed.
接收到信息: consumer ack mode test..., deliveryTag: 89
业务逻辑处理!
2024-11-17T15:19:11.477+08:00  INFO 22936 --- [rabbitmq-extensions-demo] [ntContainer#0-2] o.s.a.r.l.SimpleMessageListenerContainer : Waiting for workers to finish.
2024-11-17T15:19:11.477+08:00  INFO 22936 --- [rabbitmq-extensions-demo] [ntContainer#0-2] o.s.a.r.l.SimpleMessageListenerContainer : Successfully waited for workers to finish.

消费者处理异常,会一直重试发送,所有仍然保留在mq中

3)AcknowledgeMode.MANUAL

⼿动确认模式下,消费者必须在成功处理消息后显式调⽤ basicAck ⽅法来确认消息.如果消
息未被确认,RabbitMQ会认为消息尚未被成功处理,并且会在消费者可⽤时重新投递该消息,这
种模式提⾼了消息处理的可靠性,因为即使消费者处理消息后失败,消息也不会丢失,⽽是可以被重新处理.

    listener:
      simple:
        acknowledge-mode: manual#消息接收确认

1)消费者 正常消费情况下 

@Component
public class AckListener {
    @RabbitListener(queues = Constants.ACK_QUEUE)
    public void handleMessage(Message message, Channel channel) throws Exception {
        //消费者逻辑
        long deliverTag = message.getMessageProperties().getDeliveryTag();
        try {
            System.out.printf("接收到信息: %s, deliveryTag: %d\n",new String(message.getBody(),"UTF-8"),deliverTag);
            //业务逻辑处理
            System.out.println("业务逻辑处理!");
            //int  num = 3/0;
            System.out.println("业务逻辑完成!");
            //肯定确认
            channel.basicAck(deliverTag,false);
        } catch (Exception e) {
            //否定确认
           channel.basicNack(deliverTag,false,true);
        }
    }
}

如果不进行确认 又会发送什么?

 当我们使用手动确认(manual)的时候,一定要手动添加上肯定确认,不然即使消费者处理成功,也不会进行确认!

 2)消费者 异常消费情况下 

@Component
public class AckListener {
    @RabbitListener(queues = Constants.ACK_QUEUE)
    public void handleMessage(Message message, Channel channel) throws Exception {
        //消费者逻辑
        long deliverTag = message.getMessageProperties().getDeliveryTag();
        try {
            System.out.printf("接收到信息: %s, deliveryTag: %d\n",new String(message.getBody(),"UTF-8"),deliverTag);
            //业务逻辑处理
            System.out.println("业务逻辑处理!");
            int  num = 3/0;
            System.out.println("业务逻辑完成!");
            //肯定确认
            channel.basicAck(deliverTag,false);
        } catch (Exception e) {
            //否定确认
           channel.basicNack(deliverTag,false,true);
        }
    }
}

 否定确认完,又会进行重新入队,会变成Ready状态

此时修改为false,不让它入队,会发生什么? 

消费者处理异常,会不停的重试 

使用manual,一定要进行手动确认


总结:

模式确认方式可靠性性能使用场景
None无确认低,可能丢失消息不关心消息是否成功消费,丢失消息可容忍的场景
Auto自动确认较低,可能丢失消息较高对丢失消息容忍度较高的场景
Manual手动确认高,消息只有成功处理才会确认较低需要确保每条消息被成功消费的场景
  • None 适用于性能要求高,但对消息丢失不敏感的场景。
  • Auto 适合那些不需要太高消息可靠性的应用,但仍然需要自动化确认机制。
  • Manual 最适合那些对消息处理的可靠性要求较高,尤其是在出现异常时需要精细控制消息是否重新入队或丢弃的场景。

选择哪种模式取决于你的具体需求,尤其是对于消息可靠性的要求以及系统的性能考虑。 


结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力!  

标签:确认,System,RabbitMQ,----,Rabbitmq,message,public,消息
From: https://blog.csdn.net/chaodddddd/article/details/143831131

相关文章

  • 20241023 模拟赛
    20241023模拟赛A.浇水考虑统计每个点被浇水了几次,容易用二维前缀和维护,最后如果这个点在对应颜色的矩阵里就扣除一个次数,最后有次数的就枯萎。B.藤养巴士赛时考虑树形dp,和树上差分解法殊途同归。设\(f_u\)表示,假设所有目标在\(u\)子树中的人都已经到了\(u\)子树中,......
  • 20241022 模拟赛
    20241022模拟赛A.枚举高手考虑dp,设\(f_{i,j}\)表示考虑到第\(i\)个数,和为\(j\)的答案,\(g_{i,j}\)表示方案数。考虑两种转移:一种是在原序列的末尾加上一个\(1\),一种是把现有的数一起加上\(1\),容易发现这样既能保证有序性又能不重不漏。时间复杂度\(O(nm)\)。最近总......
  • 概念篇之认识测试(含面试、笔试题)
    目录1.什么是测试1.1生活中的测试1.2软件测试的定义2.测试的岗位(含面试、笔试题)3.软件测试和开发的区别(面试题)4.优秀的测试人员标准(面试题)概念篇除了要掌握专业术语和概念外,还有很多软件测试的方法外,更要具备测试的思想1.什么是测试1.1生活......
  • 抽象类 & 接口比较
    抽象类概述:1.该类的概念是抽象的,如形状,生物这些概念,无法实例化一个对象,所以抽象类是不允许创建对象的;2.对于这种类,这里面的大多数方法如果不通过继承的方式对类里面的方法进行重写的话,是无法执行的,比如一个形状类,既然有形状,就因该有显示名字方法,计算面积方法,在一些特定的坐标系上......
  • Linux设置socks代理
    公司里绝大多数主机已经禁止外网访问,仅保留一台主机设置socks作为代理服务器。如下为对socks这一概念的学习整理什么是socks是OSI模型下会话层的协议,位于表示层与传输层之间,作用是:exchangesnetworkpacketsbetweenaclientandserverthroughaproxyserver出现......
  • Hadoop伪分布式搭建教程
    Hadoop伪分布式在Linux上的搭建教程目录前言 前期工具准备一、JDK、Hadoop的安装二、JDK配置2.1配置环境变量2.2.使配置文件生效2.3查看Java版本 三、Hadoop配置文件3.1编辑hadoop-env.sh3.2编辑yarn-env.sh3.3编辑core-site.xml3.4编辑hdfs-site.xml3.5编......
  • 申论答案
                    应用文             城市建设当平衡好“有”和“无”的关系 “凿户牖以为室,当其无,有室之用。故有之以为利,无之以为用。”几千年前老子就指出建造房屋当平衡......
  • 韩顺平Java坦克大战项目分析与实现
    JAVA坦克大战项目文章目录界面技术栈项目结构Tank坦克父类EnemyTank敌人坦克类Hero己方坦克类Bomb爆炸类Shot子弹类MyPanel面板类AePlayWave坦克大战音乐类Node类Recorder记录类程序入口类功能子弹移动(线程)坦克移动(线程)每隔100毫秒,重绘区域,刷新绘图区(......
  • 在 SQL Server 中,创建表时可以直接为字段添加唯一约束(UNIQUE)
    在SQLServer中,创建表时可以直接为字段添加唯一约束(UNIQUE)。在CREATETABLE语句中,定义字段时,可以使用UNIQUE关键字来确保该字段的值唯一。语法:sqlCREATETABLEyour_table(aINTUNIQUE,--其他字段);假设你需要创建一个表employees,其中字段email需要保......
  • 纯C实现内存池
    1.介绍    内存池,池式结构三幻神之一,它拥有池式结构的设计初衷,为了管理和重用一组初始化的对象或资源,但作者认为,它更重要的初衷应该是一种虚拟内存的管理组件,对于需要长时间运行的程序尽可能的避免出现内存碎片。2.设计思路    内存池可以分为两种,定长和不......