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 保证消息可靠性的关键机制之一。

相关推荐
快乐吃手手 : )6 小时前
RabbitMQ(补档)
分布式·rabbitmq
别说我什么都不会7 小时前
OpenHarmony源码分析之分布式软总线:authmanager模块(1)/设备认证连接管理
分布式·操作系统·harmonyos
青云交9 小时前
Java 大视界 -- 基于 Java 的大数据分布式存储系统的数据备份与恢复策略(139)
java·大数据·分布式·数据恢复·数据备份·分布式存储·并行处理
别说我什么都不会10 小时前
OpenHarmony源码分析之分布式软总线:os_adapter模块解析
分布式·harmonyos
Hover_Z_快跑12 小时前
RabbitMQ 集群降配
分布式·rabbitmq
nlog3n13 小时前
RabbitMQ八股文
分布式·rabbitmq
熏鱼的小迷弟Liu13 小时前
【RabbitMQ】RabbitMQ中死信交换机是什么?延迟队列呢?有哪些应用场景?
分布式·rabbitmq·ruby
熏鱼的小迷弟Liu13 小时前
【RabbitMQ】RabbitMQ如何保证消息不丢失?
分布式·rabbitmq
Gold Steps.13 小时前
Zookeeper 集群部署与管理实践
linux·分布式·zookeeper·云原生
初学者杰克13 小时前
记录一次,rabbitmq开启stomp插件之后,还是连不上15674端口的问题
分布式·rabbitmq