RabbitMQ高级特性--消息确认机制

目录

一、消息确认

1.消息确认机制

2.手动确认方法

二、代码示例

1. AcknowledgeMode.NONE

1.1 配置文件

1.2 生产者

 1.3 消费者

1.4 运行程序 

 2.AcknowledgeMode.AUTO

3.AcknowledgeMode.MANUAL


一、消息确认

1.消息确认机制

生产者发送消息之后,到达消费端之后,可能会有以下情况:

1. 消息处理成功;

2. 消息处理异常。

RabbitMQ向消费者发送消息后,就会把这条消息删除掉,那么第二种情况就会造成消息丢失。

那么如何确保消息端已经被成功接收了并且被正确处理了呢?

为了确保消息从队列可靠的到达消费者,RabbitMQ提供了消息确认机制(Messageacknowledment)。

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

自动确认:当autoAck等于true时,RabbitMQ会自动把发送出去的消息置为确认,然后从内存(或者磁盘)中删除,而不管消费者是否真正的接收到消息,自动确认模式适用于对于消息可靠性要求不高的场景。

手动确认:当autoAck等于false时,RabbitMQ会等待消费者显式的调用BasicAck命令,回复确认信号后才从内存(或者磁盘)中删除,这种方式适用于对消息可靠性要求较高的场景。

自动确认代码示例:

DefaultConsumer consumer = new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, 
AMQP.BasicProperties properties, byte[] body) throws IOException {System.out.println("接收到消息: " + new String(body));}
};
channel.basicConsume(Constants.TOPIC_QUEUE_NAME1, true, consumer);

当autoAck参数置为false,对于RabbitMQ服务器来说,队列中的消息分为了两个部分:

一是等待发送给消费者的消息;二是已经发送给消费者,但是还没收到消费者确认信号的消息。

如果RabbitMQ一直没有收到消费者的确认信号,并且消费此消息的消费者已经断开连接,则RabbitMQ会重新安排这条消息进入队列,等待投递给下一个消费者,当然也有可能是原来的那个消费者。

从RabbitMQ的Web管理平台上也可以看到当前队列中Ready状态和Unacked状态的消息数。 

Ready:等待投递给消费者的消息数。

Unacked:已经投递给消费者,但是未收到消费者确认信号的消息数。

2.手动确认方法

消费者在收到消息后,可以选择确认,也可以选择跳过或者直接拒绝确认,RabbitMQ也提供了不同的确认方法,消费者客户端可以调用与其对应的channel的相关方法,共有以下三种:

肯定确认: Channel.basicAck(long deliveryTag, boolean multiple);

RabbitMQ 已经知道该消息并且成功的处理消息,可以将其丢弃。

参数说明:

deliveryTag:消息的唯一标识,它是一个单调递增的64位的长整型值,deliveryTag是每个信道(Channel)独立维护的,所以在每个信道上都是唯一的,当消费者确认(ack)一条消息时,必须使用对应的信道进行确认。

multiple:是否批量确认,在某些情况下,为了减少网络流量,可以对一系列连续的deliveryTag进行批量确认,值为true则会一次性ack所以小于等于指定deliveryTag的消息,值为false,则只确认当前deliveryTag的消息。

deliveryTag 是RabbitMQ中消息确认机制的⼀个重要组成部分, 它确保了消息传递的可靠性和顺
序性。
否定确认: Channel.basicReject(long deliveryTag, boolean requeue);

参数说明:

deliveryTag:参考上文。

requeue:表示拒绝后,这条消息该如何处理,如果值为true那么,则RabbitMQ会将这条消息重新入队,重新发送给下一个订阅的消费者,值为false,则RabbitMQ会把这条消息从队列中移除,不会再发送给消费者。

否定确认: Channel.basicNack(long deliveryTag, boolean multiple,
boolean requeue);

参数说明:

参考上文 

multiple参数设置为true则表⽰拒绝deliveryTag编号之前所有未被当前消费者确认的消息。

二、代码示例

我们基于SpringBoot来演示消息的确认机制,使用方式和方法与RabbitMQ Java Client有一定差异,

Spring AMQP对消息确认提供了三种策略:

public enum AcknowledgeMode {NONE,MANUAL,AUTO;
}
. AcknowledgeMode.NONE:
这种模式下, 消息⼀旦投递给消费者, 不管消费者是否成功处理了消息, RabbitMQ 就会⾃动确认
消息, 从RabbitMQ队列中移除消息. 如果消费者处理消息失败, 消息可能会丢失.
  AcknowledgeMode.AUTO(默认):
这种模式下, 消费者在消息处理成功时会⾃动确认消息, 但如果处理过程中抛出了异常, 则不会确
认消息.
  AcknowledgeMode.MANUAL:
⼿动确认模式下, 消费者必须在成功处理消息后显式调⽤ basicAck ⽅法来确认消息. 如果消
息未被确认, RabbitMQ 会认为消息尚未被成功处理, 并且会在消费者可⽤时重新投递该消息, 这
种模式提⾼了消息处理的可靠性, 因为即使消费者处理消息后失败, 消息也不会丢失, ⽽是可以被
重新处理.

1. AcknowledgeMode.NONE

1.1 配置文件

spring:rabbitmq:addresses: amqp:listener:simple:acknowledge-mode: none

1.2 生产者

public class Constant {public static final String ACK_EXCHANGE_NAME = "ack_exchange";public static final String ACK_QUEUE = "ack_queue";
}
/*
以下为消费端⼿动应答代码⽰例配置
*/
@Bean("ackExchange")
public Exchange ackExchange(){return
ExchangeBuilder.topicExchange(Constant.ACK_EXCHANGE_NAME).durable(true).build()
;
}
//2. 队列
@Bean("ackQueue")
public Queue ackQueue() {return QueueBuilder.durable(Constant.ACK_QUEUE).build();
}
//3. 队列和交换机绑定 Binding
@Bean("ackBinding")
public Binding ackBinding(@Qualifier("ackExchange") Exchange exchange, 
@Qualifier("ackQueue") Queue queue) {return BindingBuilder.bind(queue).to(exchange).with("ack").noargs();
}
import com.xiaowu.rabbitmq.constant.Constant;
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;
@RestController
@RequestMapping("/producer")
public class ProductController {@Autowiredprivate RabbitTemplate rabbitTemplate;@RequestMapping("/ack")public String ack(){rabbitTemplate.convertAndSend(Constant.ACK_EXCHANGE_NAME, "ack", 
"consumer ack test...");return "发送成功!";}
}

 1.3 消费者

import com.xiaowu.rabbitmq.constant.Constant;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class AckQueueListener {//指定监听队列的名称@RabbitListener(queues = Constant.ACK_QUEUE)public void ListenerQueue(Message message, Channel channel) throws
Exception {System.out.printf("接收到消息: %s, deliveryTag: %d%n", new
String(message.getBody(),"UTF-8"), 
message.getMessageProperties().getDeliveryTag());//模拟处理失败int num = 3/0;System.out.println("处理完成");}
}

1.4 运行程序 

启动生产者可以从RabbitMQ Web管理界面看到如下:

再启动消费者,控制台输出:

接收到消息: consumer ack test..., deliveryTag: 1
2024-04-29T17:03:57.797+08:00 WARN 16952 --- [ntContainer#0-1] 
s.a.r.l.ConditionalRejectingErrorHandler : Execution of Rabbit message 
listener failed.
org.springframework.amqp.rabbit.support.ListenerExecutionFailedException: 
Listener method 'public void 
com.xiaowu.rabbitmq.listener.AckQueueListener.ListenerQueue(org.springframework.a
mqp.core.Message,com.rabbitmq.client.Channel) throws java.lang.Exception'
threw exception
//....

管理界面:

可以看到消息处理失败但是消息已经从管理界面移除。 

 2.AcknowledgeMode.AUTO

将配置文件修改为:

spring:rabbitmq:addresses: amqp:listener:simple:acknowledge-mode: auto

再次启动程序,控制台不断输出错误信息:

接收到消息: consumer ack test..., deliveryTag: 1
2024-04-29T17:07:06.114+08:00 WARN 16488 --- [ntContainer#0-1] 
s.a.r.l.ConditionalRejectingErrorHandler : Execution of Rabbit message 
listener failed.
org.springframework.amqp.rabbit.support.ListenerExecutionFailedException: 
Listener method 'public void 
com.xiaowu.rabbitmq.listener.AckQueueListener.ListenerQueue(org.springframework.a
mqp.core.Message,com.rabbitmq.client.Channel) throws java.lang.Exception'
threw exception接收到消息: consumer ack test..., deliveryTag: 2
2024-04-29T17:07:07.161+08:00 WARN 16488 --- [ntContainer#0-1] 
s.a.r.l.ConditionalRejectingErrorHandler : Execution of Rabbit message 
listener failed.
org.springframework.amqp.rabbit.support.ListenerExecutionFailedException: 
Listener method 'public void 
com.xiaowu.rabbitmq.listener.AckQueueListener.ListenerQueue(org.springframework.a
mqp.core.Message,com.rabbitmq.client.Channel) throws java.lang.Exception'
threw exception接收到消息: consumer ack test..., deliveryTag: 3
2024-04-29T17:07:08.208+08:00 WARN 16488 --- [ntContainer#0-1] 
s.a.r.l.ConditionalRejectingErrorHandler : Execution of Rabbit message 
listener failed.
org.springframework.amqp.rabbit.support.ListenerExecutionFailedException: 
Listener method 'public void 
com.xiaowu.rabbitmq.listener.AckQueueListener.ListenerQueue(org.springframework.a
mqp.core.Message,com.rabbitmq.client.Channel) throws java.lang.Exception'
threw exception接收到消息: consumer ack test..., deliveryTag: 4
2024-04-29T17:07:09.254+08:00 WARN 16488 --- [ntContainer#0-1] 
s.a.r.l.ConditionalRejectingErrorHandler : Execution of Rabbit message 
listener failed.
org.springframework.amqp.rabbit.support.ListenerExecutionFailedException: 
Listener method 'public void 
com.xiaowu.rabbitmq.listener.AckQueueListener.ListenerQueue(org.springframework.a
mqp.core.Message,com.rabbitmq.client.Channel) throws java.lang.Exception'
threw exception
从⽇志上可以看出, 当消费者出现异常时, RabbitMQ会不断的重发. 由于异常,多次重试还是失败,消 息没被确认,也无法nack,就⼀直是unacked状态,导致消息积压。

3.AcknowledgeMode.MANUAL

spring:rabbitmq:addresses: amqp:listener:simple:acknowledge-mode: manual

消费者手动确认逻辑:

import com.xiaowu.rabbitmq.constant.Constant;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class AckQueueListener {//指定监听队列的名称@RabbitListener(queues = Constant.ACK_QUEUE)public void ListenerQueue(Message message, Channel channel) throws
Exception {long deliveryTag = message.getMessageProperties().getDeliveryTag();try {//1. 接收消息System.out.printf("接收到消息: %s, deliveryTag: %d%n", new
String(message.getBody(),"UTF-8"), 
message.getMessageProperties().getDeliveryTag());//2. 处理业务逻辑System.out.println("处理业务逻辑");//⼿动设置⼀个异常, 来测试异常拒绝机制
// int num = 3/0;//3. ⼿动签收channel.basicAck(deliveryTag, true);} catch (Exception e) {//4. 异常了就拒绝签收//第三个参数requeue, 是否重新发送, 如果为true, 则会重新发送,,若为false, 
则直接丢弃channel.basicNack(deliveryTag, true,true);}}
}

 这个代码运行的结果是正常的, 运行后消息会被签收: Ready为0, unacked为0。

异常时拒绝:

import com.xiaowu.rabbitmq.constant.Constant;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class AckQueueListener {//指定监听队列的名称@RabbitListener(queues = Constant.ACK_QUEUE)public void ListenerQueue(Message message, Channel channel) throws
Exception {long deliveryTag = message.getMessageProperties().getDeliveryTag();try {//1. 接收消息System.out.printf("接收到消息: %s, deliveryTag: %d%n", new
String(message.getBody(),"UTF-8"), 
message.getMessageProperties().getDeliveryTag());//2. 处理业务逻辑System.out.println("处理业务逻辑");//⼿动设置⼀个异常, 来测试异常拒绝机制int num = 3/0;//3. ⼿动签收channel.basicAck(deliveryTag, true);} catch (Exception e) {//4. 异常了就拒绝签收//第三个参数requeue, 是否重新发送, 如果为true, 则会重新发送,,若为false, 
则直接丢弃channel.basicNack(deliveryTag, true,true);}}
}
运⾏结果: 消费异常时不断重试, deliveryTag 从1递增
控制台日志:
接收到消息: consumer ack test..., deliveryTag: 1
处理业务逻辑
接收到消息: consumer ack test..., deliveryTag: 2
处理业务逻辑
接收到消息: consumer ack test..., deliveryTag: 3
处理业务逻辑
接收到消息: consumer ack test..., deliveryTag: 4
处理业务逻辑
接收到消息: consumer ack test..., deliveryTag: 5
处理业务逻辑
接收到消息: consumer ack test..., deliveryTag: 6
处理业务逻辑

管理页面上unacked也是1:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/31068.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NodeJS学习笔记

NodeJS软件安装 node环境安装: https://nodejs.org 安装好后的node通常在C:\Program Files\nodejs验证安装是否成功 node -v npm -v 进入REPL模式命令行模式 nodeNodeJS在REPL模式和编辑器使用 windos在dos下常用命令 windos命令: 1、cmd dos系统2、…

Unity 实现在模型表面进行绘画

Texture2D-SetPixels32 - Unity 脚本 API 实现思路 从笔尖位置发射射线获取画纸上碰撞位置的UV 纹理坐标以确认笔迹位置,射线检查碰撞的最大距离即笔尖距离画纸的最大距离,利用Texture2D.SetPixels32 实现对画纸贴图颜色的修改。 核心代码 发射一条以…

2008-2024年中国手机基站数据/中国移动通信基站数据

2008-2024年中国手机基站数据/中国移动通信基站数据 1、时间:2008-2024年 2、来源:OpenCelliD 3、指标:网络类型、网络代数、移动国家/地区、移动网络代码、区域代码、小区标识、单元标识、坐标经度、坐标纬度、覆盖范围、测量样本数、坐标…

LeetCode Hot100刷题——反转链表(迭代+递归)

206.反转链表 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例 1: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1]示例 2: 输入:head [1,2] 输出:[2,1]示例 3&#…

Android项目优化同步速度

最近项目需要使用ffmpeg,需要gradle配置引入ffmpeg库,发现原来通过google官方的代码仓,下载太慢了,每秒KB级别的速度。(之前下gradle/gradle plugin都不至于这么慢),于是想到配置国内镜像源来提…

Upload-Labs-Linux 1-20

前端校验绕过&#xff1a;pass 01 两种思路&#xff1a;1.通过抓包&#xff0c;修改后缀 2.前端禁用js绕过前端后缀检验 首先写一个木马&#xff0c;改为图片格式GIF89a<?php eval($_POST[cmd])?>抓包之后改为PHP格式&#xff1a; 使用蚁剑连接木马&#xff0c;第一次尝…

爬虫案例九js逆向爬取CBA中国篮球网

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、CBA网站分析二、代码 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 爬取CBA中国篮球网 提示&#xff1a;以下是本篇文章正文内容…

江科大51单片机笔记【10】蜂鸣器(上)

一、蜂鸣器 1.原理 蜂鸣器是一种将电信号转换为声音信号的器件&#xff0c;常同来产生设备的按键音、报警音等提示信号蜂鸣器按驱动方式可分为有源蜂鸣器和无源蜂鸣器&#xff08;外观基本一样&#xff09;有源蜂鸣器&#xff1a;内部自带振荡源&#xff0c;将正负极接上直流…

VS Code连接服务器教程

VS Code是什么 VS Code&#xff08;全称 Visual Studio Code&#xff09;是一款由微软推出的免费、开源、跨平台的代码编辑神器。VS Code 支持 所有主流操作系统&#xff0c;拥有强大的功能和灵活的扩展性。 官网&#xff1a;https://code.visualstudio.com/插件市场&#xff1…

使用QT + 文件IO + 鼠标拖拽事件 + 线程 ,实现大文件的传输

第一题、使用qss&#xff0c;通过线程&#xff0c;使进度条自己动起来 mythread.h #ifndef MYTHREAD_H #define MYTHREAD_H#include <QObject> #include <QThread> #include <QDebug>class mythread : public QThread {Q_OBJECT public:mythread(QObject* …

本地搭建DeepSeek R1模型 + 前端

本地搭建DeepSeek R1模型 前端 配置&#xff1a; 操作系统&#xff1a;windows11 CPU&#xff1a;i5 13600KF GPU&#xff1a;英伟达4070 12GB 内存&#xff1a;16G DDR5 硬盘&#xff1a;1TB 模型安装 本文采用Ollama进行安装。Ollama安装比较简单。 官网&#xff1…

[项目]基于FreeRTOS的STM32四轴飞行器: 五.Motor驱动

基于FreeRTOS的STM32四轴飞行器: 五.Motor驱动 一.配置CubeMX二.Motor驱动3.bug修改 一.配置CubeMX 观察motor原理图引脚对应的TIM&#xff1a; 使用内部时钟&#xff0c;配置4分频&#xff0c;后为18M&#xff0c;在设置Counter Period为1000-1&#xff0c;之后频率为18K&am…

Electron-Forge + Vue3 项目初始化

本人对Electron的浅薄理解如下图所示 由上图可以&#xff0c;如果你需要开发一个electron应用&#xff0c;你得具备基本的前端开发经验。对于electron相关的知识&#xff0c;建议先了解下基本的窗口操作&#xff0c;比如新建窗口、关闭窗口等简单的操作&#xff0c;这些内容在…

神经网络为什么要用 ReLU 增加非线性?

在神经网络中使用 ReLU&#xff08;Rectified Linear Unit&#xff09; 作为激活函数的主要目的是引入非线性&#xff0c;这是神经网络能够学习复杂模式和解决非线性问题的关键。 1. 为什么需要非线性&#xff1f; 1.1 线性模型的局限性 如果神经网络只使用线性激活函数&…

手写Tomcat:实现基本功能

首先&#xff0c;Tomcat是一个软件&#xff0c;所有的项目都能在Tomcat上加载运行&#xff0c;Tomcat最核心的就是Servlet集合&#xff0c;本身就是HashMap。Tomcat需要支持Servlet&#xff0c;所以有servlet底层的资源&#xff1a;HttpServlet抽象类、HttpRequest和HttpRespon…

PyTorch系列教程:编写高效模型训练流程

当使用PyTorch开发机器学习模型时&#xff0c;建立一个有效的训练循环是至关重要的。这个过程包括组织和执行对数据、参数和计算资源的操作序列。让我们深入了解关键组件&#xff0c;并演示如何构建一个精细的训练循环流程&#xff0c;有效地处理数据处理&#xff0c;向前和向后…

Linux系统基于ARM平台的LVGL移植

软硬件介绍&#xff1a;Ubuntu 20.04 ARM 和&#xff08;Cortex-A53架构&#xff09;开发板 基本原理 LVGL图形库是支持使用Linux系统的Framebuffer帧缓冲设备实现的&#xff0c;如果想要实现在ARM开发板上运行LVGL图形库&#xff0c;那么就需要把LVGL图形库提供的关于帧缓冲设…

Consensus 大会全观察:政策、生态与技术交汇,香港能否抢占 Web3 先机?

被誉为 “区块链界超级碗” 和 “Web3 世界杯” 的全球顶级行业峰会 —— Consensus 大会&#xff0c;在诞生十年之际首次跨越太平洋登陆亚洲&#xff0c;于 2025 年 2 月 18 日至 20 日在香港会议展览中心盛大启幕。大会汇聚了亚洲主要金融政策制定者、加密领域思想领袖、投资…

hadoop集群环境配置

目录 VMware虚拟机安装 Xshell安装 网络问题 centos7下载 ---------参考以下视频步骤进行生态搭建---------- 搭建好hadoop01 克隆出hadoop02、hadoop03 启动三台虚拟机 打开终端 输入 记录下各个ip 打开Xshell&#xff0c;新建会话 修改主机名 配置静态IP 主机名称…

C++之list

list是链表的意思&#xff0c;由一个个节点组成 一、基本接口使用&#xff1a; &#xff08;1&#xff09;与vector相同&#xff0c;有个尾插&#xff0c;也可以使用迭代器遍历&#xff1a; void test_list1() {list<int> lt;lt.push_back(1);lt.push_back(2);lt.push…