RabbitMQ 补偿机制的办法

rabbitMQ 消息顺序性、消息幂等性、消息不丢失、最终一致性、补偿机制、消息队列设计 消息顺序性、消息幂等性、消息不丢失、最终一致性、补偿机制、消息队列设计 阅读详情

1. 场景

先看这么几个面试题:

  1. 如何保证消息的可靠性投递?即如何确定消息是否发送成功?
  2. 如果失败如何处理(补偿机制)?
  3. 如何保证消息不被重复消费?或者说,如何保证消息消费时的幂等性?

2.消息的可靠性投递

消息确认

消息确认包括主要 生产者发送确认 和 消费者接受确认 ,因为发送消息的过程中我们是无法确认消息是否能路由等,一旦消息丢失我们就无法处理,所以需要确认消息,避免消息丢失。

2.1 生产者确认

我们知道生产者与消费者是完全隔离的,不做任何配置的情况下,生产者是不知道消息是否真正到达 RabbitMQ,也就是说消息发布操作不返回任何消息给生产者。

那么怎么保证我们消息发布的 可靠性投递 ?有以下几种常用机制。

由于之前的文章对上面都有过介绍,所以这里不一一介绍,而一般采用的方式就是 发布者确认模式(生产者确认模式) 。

原理:生产者将信道设置成 confirm 模式,一旦信道进入 confirm 模式,所有在该信道上面发布的消息都将会被指派一个唯一的 ID(从 1 开始),由这个 id 在生产者和 RabbitMQ 之间进行消息的确认。

这里的 唯一 ID 能够唯一标识消息,在消息不可达的时候触发回调时可以获取该值,进行对应的错误处理,建立对应的消息补偿机制。(记住这个唯一 ID,且是全局唯一,分布式系统中可采用雪花算法等方式)

confirm 模式最大的好处在于他可以是异步的,一旦发布一条消息,生产者应用程序就可以在等信道返回确认的同时继续发送下一条消息,当消息最终得到确认之后,生产者应用便可以通过回调方法来处理该确认消息,如果 RabbitMQ 因为自身内部错误导致消息丢失,就会发送一条 nack 消息,生产者应用程序同样可以在回调方法中处理该 nack 消息决定下一步的处理。

注:这里描述的场景都是那样 正确路由到队列中 的,也就是不考虑失败通知(ReturnCallback)的情况。

由于现在大家开发基本都是通过 Spring Boot 的方式进行开发,所以,这里直接提供其基本配置类参考,如下:

/** * @description : 消息生产者 */@Component@Slf4jpublic class RabbitmqProducer {    @Autowired    private RabbitTemplate rabbitTemplate;    public void sendMessage(Map<String, Object> headers, Object message, String messageId, String exchangeName, String key) {        // 自定义消息头        MessageHeaders messageHeaders = new MessageHeaders(headers);        // 创建消息        Message<Object> msg = MessageBuilder.createMessage(message, messageHeaders);        /* 确认的回调 确认消息是否到达 Broker 服务器 其实就是是否到达交换器         * 如果发送时候指定的交换器不存在 ack 就是 false 代表消息不可达         */        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {            log.info("correlationData:{} , ack:{}", correlationData.getId(), ack);            if (!ack) {                System.out.println("进行对应的消息补偿机制");            }        });        // 在实际中ID 应该是全局唯一 能够唯一标识消息 消息不可达的时候触发ConfirmCallback回调方法时可以获取该值,进行对应的错误处理        CorrelationData correlationData = new CorrelationData(messageId);        rabbitTemplate.convertAndSend(exchangeName, key, msg, correlationData);    }}

复制代码

2.2 消费者确认

说了生产者如何保证消息的确认,而对于消费者来说,同样需要确认。

前面也说了目前编码都是基于 Spring 的,那么对于消费者来说,同样也有一个接收消息的配置类,如下:

/** * @description : 消息消费者 */@Component@Slf4jpublic class RabbitmqConsumer {    @RabbitListener(bindings = @QueueBinding(            value = @Queue(value = RabbitInfo.QUEUE_NAME, durable = RabbitInfo.QUEUE_DURABLE),            exchange = @Exchange(value = RabbitInfo.EXCHANGE_NAME, type = RabbitInfo.EXCHANGE_TYPE),            key = RabbitInfo.ROUTING_KEY)    )    @RabbitHandler    public void onMessage(Message message, Channel channel) throws Exception {        MessageHeaders headers = message.getHeaders();        // 获取消息头信息和消息体        log.info("msgInfo:{} ; payload:{} ", headers.get("msgInfo"), message.getPayload());    }}

复制代码

对于上面接收消息的配置并没有做任何配置,当我们发送消息的时候,消费者接收消息并进行对应的逻辑处理,并且 Spring 的处理是自动 ack 的 ,但其实它也有配置,如下:

spring:  rabbitmq:    addresses: 127.0.0.1:5672    # RabbitMQ 默认的用户名和密码都是guest 而虚拟主机名称是 "/"    # 如果配置其他虚拟主机地址,需要预先用管控台或者图形界面创建 图形界面地址 http://主机地址:15672    username: admin    password: admin    virtual-host: /    listener:      simple:        # 为了保证信息能够被正确消费,建议签收模式设置为手工签收,并在代码中实现手工签收        acknowledge-mode: manual        # 侦听器调用者线程的最小数量        concurrency: 10        # 侦听器调用者线程的最大数量        max-concurrency: 50

复制代码

也就是上面的 acknowledge-mode ,他有三个值,如下:

 

  1. 当为 NONE 的时候,即默认值,即 autoAck=true ,消费者接收消息后自动确认,此时,MQ 队列中的消息会移除;
  2. 当为 MANUAL 的时候,需要我们手动确认,即 channel.basicAck ,如下:
  3. 当为 AUTO 的时候,经测试发现和 NONE 没什么不同 。

此时,有人就会说那么如果在默认情况下(自动确认),我们的业务代码抛出异常了怎么办?

Spring 的做法是会抛出异常,并且消息不会被 ack,如下面这种情况:

1/0 肯定会抛出异常,此时会一直打印日志,如下:

 

再去看 MQ 客户端,状态如下:

此时,可能会造成重复消费,怎么理解?

假如我的业务代码没有事务,或者在参数的传递过程中某个方法没有事务的控制,当异常业务代码之前入库了,那么这条消息实际上是没有被确认的,还在队列中,因此,当下次程序启动,则会再次消费这条消息,尽管业务代码出现了异常。

自动确认会在消息发送给消费者后立即确认,但存在丢失消息的可能,如果消费端业务代码抛出异常,也就是消费端没有处理成功这条消息,那么就 相当于丢失了消息 。

如果消息已经被处理,但后续代码抛出异常,使用 Spring 进行管理的话消费端业务代码会进行回滚,这也同样造成了实际意义的消息丢失。

3. 补偿机制

在回到前面的问题,如何确定消息是否发送成功? 生产者确认机制 确实能帮我们解决这个问题,但如果生产者就是接收不到 ack 这个指令怎么办,比如消费者处理时间太长或者网络超时,等等情况,导致生产者一直接收不到这个 ack ,此时怎么办?

生产者与消费者之间应该约定一个超时时间,比如 5 分钟,对于超出这个时间没有得到响应的消息,可以设置一个定时重发的补偿机制:通过消息落库 + 定时任务来实现。

怎么做?这里讲讲思路,如下:

  1. 发送消息之前,先把消息入库,我这里的表设计如下:
   CREATE TABLE `t_cap_published_message` (     `id` varchar(40) COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '标识。',     `version` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '版本',     `exchange` varchar(200) COLLATE utf8mb4_bin DEFAULT '' COMMENT '交换机。',     `topic` varchar(200) COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '话题。',     `content` longtext COLLATE utf8mb4_bin NOT NULL COMMENT '消息内容。',     `retries` int(11) NOT NULL COMMENT '重试次数,一般为 3 次。',     `expiry` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '过期时间。',     `status` varchar(40) COLLATE utf8mb4_bin NOT NULL COMMENT '状态,成功则消息ack成功,其他状态都要重试。',     `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间。',     `last_modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间,可以用作数据版本。',     PRIMARY KEY (`id`)   ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='发布的消息。';

复制代码

  1. 入库之后在发送消息;
  2. 如果在规定时间不能 ack 或者 ack=false ,即 confirmCallback 回调的 ack=false ,则按照定时规则重新发送消息;
  3. 然后对于发布成功的消息,如果业务操作完成,实际上它的作用已经发挥完成,一段时间对数据库做清理即可,根据业务的具体情况。

4. 消息幂等性

首先我们要知道什么是幂等性,比如一个转账系统,A 要转给 B 100 元,当 A 发出消息后,B 接收成功,然后给 MQ 确认的时候出现网络波动,MQ 并没有接收到 ack 确认,那 MQ 为了保证消息被消费,就会继续给消费者投递之前的消息,如果再重复投递 5 次,则 B 在处理 5 次,加上之前的一次,B 的余额增加了 600 元,很明显是不合理的。

所以幂等性简单来说就是: 重复调用多次产生的业务结果与调用一次产生的业务结果相同 ;

为了避免相同消息的重复处理,必须要采取一定的措施。RabbitMQ 服务端是没有这种控制的,因为它不知道你是不是就要把一条消息发送两次,所以只能在消费端控制。

回到前面生产者确认模式中讲到了一个 全局唯一 ID ,我们可以通过他来保证消息的幂等性,如下:

  1. 消费者获取到消息后先根据这个全局 唯一 ID 去查询 redis/db 是否存在该消息;
  2. 如果不存在,则正常消费,消费完毕后写入 redis/db;
  3. 如果存在,则证明消息被消费过,直接丢弃,不做处理
RabbitMQ的高级特性和消息补偿机制,java高级面试 死信队列,英文缩写:DLX 。Dead Letter Exchange(死信交换机),当消息成为Dead message (死信)后,可以被重新发送到另一个交换机,这个交换机就是DLX。 #####消息成为死信的三种情况: 1.队列消息长度到达限制; 2.消费者拒接消费消息,并且不把消息重新放入原目标队列; 3.原队列存在消息过期设置,消息到达超时时间未被消费; #####死信队列和死信交换机: 死信队列和死信交换机与正常的队列和交换机一模一样, 没有任何区别 !! 如何实现队列与死信交换机绑定 , 给队 阅读详情

相关推荐

RabbitMQ的高级特性和消息补偿机制

##前言 今天来说说RabbitMQ的高级特性和消息补偿机制 1. 消息的可靠性投递 在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。 RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。 confirm 确认模式:当消息从 producer 发送到 exchange 则会执行 confirmCallback中的confirm方法。 return 退回模式:当消息发送给Exchange后, Exchange将消息路由到queue失败会执行Return

m0_56252652的博客 1092

RabbitMQ的消息补偿机制

文章目录 前言 一、pandas是什么? 二、使用步骤 1.引入库 2.读入数据 总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。 提示:以下是本篇文章正文内容,下面案例可供参考 一、pandas是什么? 示例:pandas 是基于NumPy 的一种工具,该工具是为了解决....

weixin_44399827的博客 1万+

RabbitMQ 补偿机制、消息幂等性解决方案

首先我们要知道什么是幂等性,比如一个转账系统,A 要转给 B 100 元,当 A 发出消息后,B 接收成功,然后给 MQ 确认的时候出现网络波动,MQ 并没有接收到 ack 确认,那 MQ 为了保证消息被消费,就会继续给消费者投递之前的消息,如果再重复投递 5 次,则 B 在处理 5 次,加上之前的一次,B 的余额增加了 600 元,很明显是不合理的。,因为发送消息的过程中我们是无法确认消息是否能路由等,一旦消息丢失我们就无法处理,所以需要确认消息,避免消息丢失。

Cr1556648487的博客 791

rabbitmq中补偿机制,死信队列

在该函数中,首先尝试处理消息的业务逻辑,若处理成功,则手动确认消息已被消费。若处理失败,则将消息发送到死信队列,并拒绝消息,将其从原始队列中移除。补偿机制是指在消息队列中,当某个消息处理失败时,可以通过一些手段进行补偿,尝试重新处理该消息,直到处理成功为止。请注意,以上示例代码仅为演示目的,实际应用中可能需要根据具体需求进行适当的修改和优化。在上述代码中,首先创建了RabbitMQ的连接和通道。然后定义了原始队列和死信队列,并通过​。在声明原始队列时,通过​。​方法开始消费原始队列中的消息,并通过​。

牛肉胡辣汤 1054

MATLAB实战:5步搞定铣削稳定性叶瓣图(附完整代码解析)

本文提供了一份基于MATLAB的铣削稳定性叶瓣图实战指南。通过5个清晰步骤,从动力学原理出发,详细解析了利用半离散法构建稳定性判据、模块化代码设计、高效参数扫描及高级可视化的完整流程,并附有完整代码,帮助工程师快速预测和规避加工颤振,优化铣削工艺参数。

weixin_29197051的博客 196

R分析|怎么样使用piecewiseSEM构建结构方程模型?保姆级教程

计算科学的发展让我们有了更先进的工具揭示自然规律。结构方程模型(SEM)已被广泛应用于多学科进行数据挖掘。其中在R中实现的结构方程模型是方便快捷的。今天要介绍的piecewiseSEM包就是R中实现SEM的一种方式。piecewiseSEM被开发出来主要目的是为了解决复杂模型简化后进行分段分析。个人认为最大的特点是更灵活,且采用的是原始数据,并不是传统的协方差矩阵。其次,将SEM破碎化为多个独立的方程,对样本量要求较少。

weixin_46814750的博客 6941

RabbitMQ的高级特性和消息补偿机制(1)

RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间。消息过期, 可以让队列统一过期, 也可以让它单独的消息过期。

youpccc的博客 767

✨从零搭建 Ubuntu22.04 + Python3.11 + PyTorch2.5.1 GPU Docker 镜像并上传 Docker Hub

构建一个带 GPU 支持的深度学习开发镜像;使用 PyTorch 2.5.1(CUDA 12.4)环境;发布并托管 Docker 镜像至 Docker Hub;验证 GPU 环境正常运行!🎉 这将极大提升你的项目部署效率,尤其是在多人协作或跨平台部署场景中。

用大模型重构生活:猫奴程序员的AI智能日常记录 1581

RabbitMQ如何保证消息的可靠性

RabbitMQ如何保证消息的可靠性

weixin_44854785的博客 4675

车辆横向控制MPC实战:从‘老王模型’到CasADi求解的保姆级避坑指南

本文详细解析了车辆横向控制MPC的实战应用,从经典动力学模型选择到CasADi求解器的优化配置,提供了一套完整的避坑指南。重点介绍了车辆动力学模型的验证要点、CasADi环境的高效配置技巧以及MPC问题建模的核心要素,帮助开发者快速实现稳健的横向控制方案。

weixin_42524499的博客 346

实战指南:如何用DCCRN模型提升语音增强效果(附代码示例)

本文提供了基于DCCRN模型进行语音增强的实战指南。通过深入解析复数网络如何协同处理语音的幅度与相位信息,并附有完整的PyTorch代码实现,帮助开发者从数据准备、模型搭建到训练调优,构建高效的实时语音增强系统,有效提升嘈杂环境下的语音清晰度。

weixin_29054245的博客 479

RabbitMQ如何保证消息可靠性?

以上介绍了RabbitMQ保证消息可靠性的问题、产生原因、解决方案等。不足之处,欢迎指正。

辣香牛肉面的博客 1448

Windows环境下IBSIMU的安装

本文主要介绍离子束仿真程序IBSIMU在Windows环境下的安装。

C_Tso的博客 2993

MQ消息的可靠性

MQ消息的可靠性 消息的可靠性即避免消息丢失。 代码以RabbitMQ为例。 Channel: void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException; String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException; 消息从Prod

haoranhaoshi的博客 1412

汽车诊断服务(UDS——0x11服务解析)

ECU复位服务(0x11)详解:该服务支持硬件/钥匙开关/软件三种复位类型(0x01-0x03),复位前需发送肯定响应。请求格式为11+子功能,响应含51+子功能。典型应用需配合安全访问流程,测试时需监控CAN总线通信及DTC状态。复位后ECU将返回默认会话,期间不处理任何诊断请求。服务在编程会话且安全解锁后执行,硬复位会初始化存储数据。

从事汽车行业 980

车间数据孤岛终于破了!C#上位机+MQTT打通产线与MES,数据延迟从小时级降到秒级

本文针对工业上位机开发中常见的车间数据与企业管理系统(MES)割裂问题,提出基于C#和MQTT协议的完整解决方案。通过EMQX MQTT Broker实现数据实时双向传输,将质量追溯时间从2小时缩短至10秒,生产计划自动下发,实现秒级数据延迟。方案采用分层解耦架构,支持设备更换和系统升级,核心优势包括低延迟、高可靠性和易扩展性。文章详细介绍了MQTT协议选型理由、整体架构设计、技术选型(EMQX Broker和MqttNet客户端),并提供了C#实现代码和部署流程。该方案已在实际产线验证,可有效解决&quo

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 484

Rabbitmq消息补偿机制

8、Q3中的延迟消息延迟时间已到,被回调检查服务接收到,之后就拿着这条延迟消息在数据库中比对,如果比对成功,证明消费者接收到了生产者的业务消息并处理成功(如果不处理成功谁会傻了吧唧发送确认消息呢);2、自定义补偿机制。由于消息补偿机制的存在,可以更加有效保证消息可以被消费,但是带来的问题是可能某个消息执行的比较久,导致同一条消息再次被发送给了消费者,而前一条消息顺利执行完,这样一条消息就会被多次执行,所以消费者端的方法需要涉及成幂等性,也就是对于一条消息,无论被消费者消费几次,效果都是一样的。

C18298182575的博客 1211

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

【RabbitMQ】补偿机制、消息幂等性、最终一致性、消息顺序性

1.补偿机制 由于生产者与消费者完全隔离,即使消费者没有接收到消息,或者消费时出现异常,生产者也是完全不知情的。所以生产者最终确定消费者有没有消费成功有两种通信方式: 消费者收到消息,处理完毕后,调用生产者的API 例如:提单系统给其他系统发送了碎屏保消息后,其他系统必须在处理完消息后调用提单系统提供的API,来修改提单系统中数据的状态。只要API没有被调用,数据状态没有被修改,提单系统就认为下游系统没有收到这条消息。 消费者收到消息,处理完毕后,发送一条响应消息给生产者 例如:商业银行与人民银行二

A minor 5075

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Spring-RabbitMQ-自动补偿机制

1. 自动补偿机制 在RabbitMQ里,如果消费者在处理消息时,业务逻辑出现异常,默认会执行补偿机制(也就是消息重试机制)。如果业务逻辑出现异常,是不会消费消息的。基于上一篇博客的例子《消息中间件系列教程(13) -RabbitMQ-SpringBoot集成RabbitMQ》来演示一下。 现在消费者处理消息的地方模拟一个异常: 生产者发送消息给消费者,会发现控制台一直在打印错误日志,也就是说,消费者一直在重试消费(补偿): 也可以在RabbitMQ控制台看到,消息时没有被消费的..

lzf的博客 915

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

RabbitMQ应用问题——消息补偿机制以及幂等性的保证简单介绍

大伙可以到我的RabbitMQ专栏获取更多信息 RabbitMQ应用问题 消息可靠性的保障 ​​​​​​​消息补偿机制 消息的幂等性保障 ​​​​​​​乐观锁解决方案 ​​​​​​​消息补偿机制 这个图看起来好像很复杂,其实只做到了三件事情: 发消息 收到消息确认 检查比对是不是收到消息了 详细说明消息补偿流程 发生业务操作,业务数据写入数据库 生产者将消息发送给MQ的队列Q1 发送了一条与step2中一摸一样的延迟消息到对了Q3 消费者监听Q1,获取到了step2中

保暖透气大裤衩LeoLee的博客 2418

RabbitMQ应用问题——消息补偿机制以及代码示例

RabbitMQ应用问题——消息补偿机制以及代码示例

qq_52183856的博客 1530

消息中间件系列教程(14) -RabbitMQ-自动补偿机制

在RabbitMQ里,如果消费者在处理消息时,业务逻辑出现异常,默认会执行补偿机制(也就是消息重试机制)。如果业务逻辑出现异常,是不会消费消息的。基于上一篇博客的例子《消息中间件系列教程(13) -RabbitMQ-SpringBoot集成RabbitMQ》来演示一下。 现在消费者处理消息的地方模拟一个异常: 生产者发送消息给消费者,会发现控制台一直在打印错误日志,也就是说,消费者一直在重试消费...

阿甘兄 3329

RabbitMQ消息不丢失补偿方案

RabbitMQ特性 异步 解耦 削峰填谷 正是由于其存在以上特性,所以被广泛应用于实际开发中,但是线上环境却存在消息丢失的场景,需要我们通过一些技术手段来保证最终数据的一致性(即MQ消息不丢失),首先我们先需要了解下RabbitMQ整体的架构图如下: RabbitMQ组成 通过上面架构图我们可以看到RabbitMQ大致分为3部分, 客户端(生产者) RabbitMQ消息中间件 服务端(消费者) RabbitMQ消息丢失原因 那么究竟哪些环节会导致消息丢失呢? 生产者发送消息到RabbitMQ服

qq_30035133的博客 2663
上一篇: 观察者模式及应用场景
下一篇: RabbitMQ 搭建完之后怎么做?
Java_ttcd
博客等级 码龄4年 198粉丝 190原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值