RabbitMQ 的 ACK 机制详解


RabbitMQ
本文是博主在记录使用 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 的示例代码,里面有这么几个代码片段:

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

### RabbitMQ适用的业务场景 #### 广播消息 通过"fanout"交换机实现广播消息的功能,可以向所有绑定的队列发送相同的消息副本。这种方式下,发送端无需关注具体的接收方,只要将消息发布出去即可[^3]。 #### 路由模式下的精准分发 利用路由键(routing key)和绑定键(binding key),能够精确控制消息传递路径。生产者依据特定条件设置路由键,只有匹配相应规则的消费者才能接收到指定消息。 #### 简单的任务委托 对于一对一的任务委派情况,采用简单的直接模式就足够了。例如,在异步处理邮件发送请求时,可以把待发送邮件的信息放入队列中等待后台的服务进程取出并执行实际操作[^4]。 #### 复杂任务分配与负载均衡 当面临大量并发任务需被均匀散布至多个工作节点上执行的情形下,则可借助于工作队列模式(work queues),它允许多个工作线程监听同一个队列,并从中领取任务来进行处理,从而达到良好的性能优化效果。 ```python import pika connection = pika.BlockingConnection(pika.ConnectionParameters('localhost')) channel = connection.channel() # 定义一个名为 'task_queue' 的持久化队列 channel.queue_declare(queue='task_queue', durable=True) def callback(ch, method, properties, body): print(f"Received {body}") ch.basic_ack(delivery_tag=method.delivery_tag) # 设置公平调度策略 channel.basic_qos(prefetch_count=1) channel.basic_consume(queue='task_queue', on_message_callback=callback) print('Waiting for messages.') channel.start_consuming() ``` 此代码片段展示了如何配置RabbitMQ以支持复杂任务分配的工作队列模式,其中包含了创建持久化的队列以及设定合理的消费机制来确保高可用性和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值