RabbitMQ 的 Ack 机制是什么?怎么合理使用它?


本文是博主在记录使用 RabbitMQ 在执行业务时遇到的问题和解决办法,因此查阅了相关资料并做了以下记载,记录了 Ack 的机制和使用要点,以及所带来的危害。


文章目录

1、消息确认 ACK 机制

在大型项目中的开发中必然会经历到数据的处理,会使用到消息中间件,而 RabbitMQ 作为主流消息中间件被应用于各大项目。RabbitMQ 核心架构包含交换机(Exchange)和队列(Queue)两大组件。交换机负责将消息按预设路由规则分发至对应队列,队列则作为消息暂存容器实现生产者与消费者的解耦。

在消息处理流程中,当消费者在处理队列中的消息时发生异常,极有可能导致当前处理的消息未完成消费确认,进而造成数据丢失。为解决这一问题,RabbitMQ提供了消息确认(ACK)机制。该机制通过要求消费者在完成消息处理后发送显式确认,确保即使发生异常情况,消息仍能保留在队列中进行重新投递,从而构建起可靠的消息传递系统。

ACK 机制是消息可靠性的保障!

那么具体的ACK的消息确认机制是什么?

ACK 机制是消费者从 MQ 收到消息并处理完成后反馈给 MQ ,MQ 收到反馈后才将此消息从队列中删除。

上面的解释传递了几种信息,下面开始分析。

  • 触发时机是在消费者消费消息后。
  • 形式是消费者对 RabbitMQ 的一个反馈行为。
  • ACK 机制成功会导致被消费的消息从队列进行清除。
  • 不成功则不会进行清除消息。

上面的分析可以看出 ACK 机制能保障了消息传递的可靠性。

2、机制工作原理

  • 消费者从 MQ 队列中获取消息,当收到消息时会使用设定好的业务逻辑开始处理消息,转换或存储。
  • 如果消费者成功处理消息,会向 MQ 发送一个 ACK 信号,告知消息已被成功消费。
  • MQ 收到 ACK 信号反馈后,会将该消息从队列中删除。
  • 如果消费者在处理消息时出现异常(如网络不稳定、服务器异常等),无法发送 ACK 信号,MQ 会认为该消息未被正常消费,将其重新放回队列。
  • 在集群环境下,MQ 会将该消息推送给其他在线的消费者,在消费者都失败后或没有消费者也会视为未被正常消费。

这种机制保证了在消费者服务端故障的时候,不丢失任何消息和任务。消息永远不会从 MQ 中删除,只有当消费者正确发送 ACK 反馈,MQ 确认收到后,消息才会从队列的中删除。

3、手动确认与自动确认

消息的 ACK 确认机制默认是打开的。有手动确认和自动确认两种。

  • 手动确认:消费者在调用 channel.basicConsume() 消费消息时,设置 autoAck=false,进入手动确认模式。此时,消费者需要显式调用 channel.basicAck() 方法来确认消息。如果处理失败,可以调用 channel.basicNack()channel.basicReject() 方法拒绝消息,RabbitMQ 会将消息重新入队。
  • 自动确认:当 autoAck=true 时,RabbitMQ 会自动将发送出去的消息置为确认状态,并从队列中删除。

自动确认方式不推荐,因为它无法保证消息真正被消费者处理成功,可能会导致消息丢失。

参数说明

  • deliveryTag:消息的唯一标识,用于确认具体的消息。
  • multiple:当设置为 true 时,表示确认当前 deliveryTag 及之前所有未确认的消息;设置为 false 时,仅确认当前的 deliveryTag
  • requeue:在拒绝消息时,如果设置为 true,消息会重新入队;如果设置为 false,消息可能会被丢弃或进入死信队列。

4、机制的注意事项

如果消费者忘记发送 ACK 信号,那么后果很严重。当消费者退出时候,消息会被一直重新分发。然后 MQ会占用越来越多的内存,从而引发 内存泄漏 ,由于 MQ 长时间运行,因此这个内存泄漏是致命的。

为避免内存泄漏,可以在开发中进行异常捕获,确保消费者程序正常执行。同时,可以通过配置 重试次数 来防止消息无限重试。

在我的另一片文章中有具体的代码示例,是关于如何使用 SpringBoot 实现 RabbitMQ 的示例代码,里面有这么几个代码片段:

java 复制代码
@Bean
public SimpleMessageListenerContainer contactSyncContainer() {
	if (!rabbitMqConfig.isUse()) {
		return null;
	}
	log.info("contact begin");
	SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
	container.setConcurrentConsumers(1);
	container.setMaxConcurrentConsumers(1);
	container.setAcknowledgeMode(AcknowledgeMode.MANUAL); // RabbitMQ默认是自动确认,这里改为手动确认消息
	//设置一个队列
	container.setQueueNames(rabbitMqConfig.getQueueOnlineInfo());
	container.setMessageListener(infoConsumer);
	log.info("contact end");
	return container;
}
  • container.setAcknowledgeMode(AcknowledgeMode.MANUAL); 这段代码是实现手动确认消息。
  • AcknowledgeMode 这个枚举类中存在三种方式:NONE、MANUAL 和 AUTO。
  • container 中具有参数 prefetchCount ,这个参数是计算在 ACK 机制前可以允许取多少条消息。
  • infoConsumer 就是我们定义的消费者,接收消息并反馈 ACK。
  • channel.basicAck(deliveryTag, false); 接收消息并返回 ACK 的方法,删除队列消息。
  • channel.basicReject(deliveryTag, false); 接收消息失败,不删除队列消息。

综上所述:RabbitMQ 的 ACK 机制通过确认消息的处理状态,确保消息的可靠传递,避免消息丢失,是 RabbitMQ 保证消息可靠性的关键机制之一。

相关推荐
初次攀爬者2 天前
RabbitMQ的消息模式和高级特性
后端·消息队列·rabbitmq
初次攀爬者4 天前
ZooKeeper 实现分布式锁的两种方式
分布式·后端·zookeeper
让我上个超影吧5 天前
消息队列——RabbitMQ(高级)
java·rabbitmq
塔中妖5 天前
Windows 安装 RabbitMQ 详细教程(含 Erlang 环境配置)
windows·rabbitmq·erlang
断手当码农5 天前
Redis 实现分布式锁的三种方式
数据库·redis·分布式
初次攀爬者5 天前
Redis分布式锁实现的三种方式-基于setnx,lua脚本和Redisson
redis·分布式·后端
业精于勤_荒于稀5 天前
物流订单系统99.99%可用性全链路容灾体系落地操作手册
分布式
Ronin3055 天前
信道管理模块和异步线程模块
开发语言·c++·rabbitmq·异步线程·信道管理
Asher05095 天前
Hadoop核心技术与实战指南
大数据·hadoop·分布式
凉凉的知识库5 天前
Go中的零值与空值,你搞懂了么?
分布式·面试·go