什么是消费者确认?
在 Spring 中操作 RabbitMQ 时,消费者确认机制的实现主要依赖 Spring AMQP(Spring 对 AMQP 协议的封装,底层基于 RabbitMQ Java 客户端)。Spring 提供了灵活的配置方式,支持自动确认和手动确认和默认确认3种模式。
使用方法
1.引入依赖
<!-- Spring AMQP 核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.配置确认模式
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
listener:
simple:
acknowledge-mode: manual # 手动确认模式 ,auto为自动,none为默认
prefetch: 1 # 每次从队列拉取1条消息,处理完再拉取下一条(避免消息堆积,手动模式需要)
default-requeue-rejected: false # 拒绝消息时是否重新入队(手动模式下可忽略,由代码控制)
3.三种默认的区别
3.1手动模式(适合核心业务)
在手动确认模式下,消费者需要通过 Channel 对象显式调用确认方法(basicAck/basicNack/basicReject)。Spring 会将消息的 Channel 和 DeliveryTag 封装到 Message 对象中,可通过参数注入获取。
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import com.rabbitmq.client.Channel;
@Component
public class RabbitConsumer {
// 监听队列:test.queue
@RabbitListener(queues = "test.queue")
public void handleMessage(String messageContent, Channel channel, Message message) throws Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag(); // 获取消息唯一标识
try {
// 1. 处理业务逻辑(例如:解析消息、操作数据库等)
System.out.println("处理消息:" + messageContent);
// 2. 处理成功,手动确认
// 参数1:deliveryTag(消息标识)
// 参数2:multiple(是否批量确认,false表示仅确认当前消息)
channel.basicAck(deliveryTag, false);
System.out.println("消息确认成功,deliveryTag: " + deliveryTag);
} catch (Exception e) {
// 3. 处理失败,拒绝消息
// basicNack支持批量拒绝,basicReject仅支持单条
// 参数3:requeue(是否重新入队,false则消息进入死信队列或被丢弃)
channel.basicNack(deliveryTag, false, false);
// 或使用 basicReject:channel.basicReject(deliveryTag, false);
System.out.println("消息处理失败,已拒绝,deliveryTag: " + deliveryTag);
}
}
}
3.2默认模式(不推荐)
默认模式下,消息只要被消费者接收,无论是否抛出异常,都会将队列的消息删除
@RabbitListener(queues = "test.queue")
public void handleMessageAutoAck(String messageContent) {
System.out.println("自动确认模式,处理消息:" + messageContent);
// 无需手动确认,消息被接收后立即从队列删除
}
3.3自动确认(适合非核心业务)
auto 模式下,Spring 会根据业务逻辑是否抛出异常自动确认或拒绝:
@RabbitListener(queues = "test.queue")
public void handleMessageAuto(String messageContent) {
System.out.println("处理消息:" + messageContent);
// 若业务逻辑无异常,Spring 自动调用 basicAck
// 若抛出异常,Spring 自动调用 basicNack(并根据 default-requeue-rejected 决定是否重新入队)
if (messageContent.contains("error")) {
throw new RuntimeException("处理失败"); // 触发自动拒绝
}
}
关键注意事项
- 手动确认的时机:必须在业务逻辑处理完成后再调用
basicAck,避免提前确认导致消息丢失(例如:处理过程中崩溃)。 - prefetch 配置:手动模式下建议设置
prefetch(每次拉取的消息数),防止消费者端堆积大量未确认消息(若消费者崩溃,这些消息会被重新投递,可能导致重复处理)。 - 死信队列配合:若消息处理失败且不需要重新入队(
requeue=false),建议配置死信队列(DLQ),将失败消息转发到 DLQ 以便后续分析,避免消息丢失。 - 幂等性处理:由于消息可能被重新投递(如消费者崩溃后重发),需确保消费者逻辑支持幂等性(重复处理不会导致业务异常)。
4.如何实现非核心业务和核心业务采用不同确认模式
核心思路
Spring AMQP 允许通过 @RabbitListener 的 containerFactory 属性 指定不同的监听器容器工厂,每个工厂可配置独立的确认模式(手动 / 自动)。因此,只需:
- 定义两个监听器容器工厂(一个手动确认,一个自动确认)。
- 核心业务的消费者指定手动确认的工厂,非核心业务指定自动确认的工厂。
步骤 1:配置两个监听器容器工厂
在配置类中定义两个 SimpleRabbitListenerContainerFactory 实例,分别对应手动确认和自动确认模式。
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
// 1. 消息转换器(JSON 序列化,全局共用)
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
// 2. 手动确认模式的容器工厂(核心业务用)
@Bean(name = "manualAckContainerFactory")
public SimpleRabbitListenerContainerFactory manualAckContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(messageConverter()); // 使用 JSON 转换器
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL); // 手动确认
factory.setPrefetchCount(1); // 每次拉取1条消息,处理完再拉取(确保可靠)
return factory;
}
// AUTO 模式的容器工厂(非核心业务用)
@Bean(name = "autoAckContainerFactory")
public SimpleRabbitListenerContainerFactory autoAckContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(new Jackson2JsonMessageConverter());
factory.setAcknowledgeMode(AcknowledgeMode.AUTO); // 关键:AUTO 模式
factory.setDefaultRequeueRejected(true); // 处理异常时自动重入队(默认值也是 true)
factory.setPrefetchCount(10); // 适当提高吞吐量
return factory;
}
}
核心业务消费者
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import com.rabbitmq.client.Channel;
@Component
public class CoreBusinessConsumer {
// 核心业务:订单处理(使用手动确认工厂)
@RabbitListener(queues = "core.order.queue", containerFactory = "manualAckContainerFactory")
public void handleOrder(String orderId, Channel channel, Message message) throws Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
// 处理核心业务(如订单支付、库存扣减)
System.out.println("处理订单:" + orderId);
// 业务处理成功,手动确认
channel.basicAck(deliveryTag, false);
} catch (Exception e) {
// 处理失败,拒绝消息(进入死信队列)
channel.basicNack(deliveryTag, false, false);
}
}
}
非核心业务
@Component
public class NonCoreConsumer {
// 非核心业务:如用户行为日志收集
@RabbitListener(queues = "noncore.behavior.queue", containerFactory = "autoAckContainerFactory")
public void handleBehaviorLog(String log) {
System.out.println("处理行为日志:" + log);
// 若处理中抛出异常(如数据库临时不可用),Spring 会自动拒绝消息并重新入队
if (log.contains("error")) {
throw new RuntimeException("临时处理失败"); // 触发自动重入队
}
}
}
RabbitMQ消费者确认详解
2588

被折叠的 条评论
为什么被折叠?



