和生产者的消息确认机制不同,因为消息接收本来就是在监听消息,符合条件的消息就会消费下来。
所以,消息接收的确认机制主要存在三种模式:
①自动确认, 这也是默认的消息确认情况。 AcknowledgeMode.NONE
RabbitMQ成功将消息发出(即将消息成功写入TCP Socket)中立即认为本次投递已经被正确处理,不管消费者端是否成功处理本次投递。
所以这种情况如果消费端消费逻辑抛出异常,也就是消费端没有处理成功这条消息,那么就相当于丢失了消息。
一般这种情况我们都是使用try catch捕捉异常后,打印日志用于追踪数据,这样找出对应数据再做后续处理。
② 根据情况确认, 这个不做介绍
③ 手动确认 , 这个比较关键,也是我们配置接收消息确认机制时,多数选择的模式。
消费者收到消息后,手动调用basic.ack/basic.nack/basic.reject后,RabbitMQ收到这些消息后,才认为本次投递成功。
basic.ack用于肯定确认
basic.nack用于否定确认(注意:这是AMQP 0-9-1的RabbitMQ扩展)
basic.reject用于否定确认,但与basic.nack相比有一个限制:一次只能拒绝单条消息
消费者端以上的3个方法都表示消息已经被正确投递,但是basic.ack表示消息已经被正确处理,但是basic.nack,basic.reject表示没有被正确处理,但是RabbitMQ中仍然需要删除这条消息。
看了上面这么多介绍,接下来我们一起配置下,看看一般的消息接收 手动确认是怎么样的。
之前介绍用了很多个交换,现在我们就先给直连型交换机添加消息接收确认机制,
新建MessageListenerConfig.java上添加代码相关的配置代码(可以看到注释掉的代码,就是给扇型交换机配置消息确认,只用在这个里面继续添加对应的队列和对应的接收类即可,当然对应的接收类也需要跟后面介绍一样加上对应方法):
@Configuration
public class MessageListenerConfig {
@Autowired
private CachingConnectionFactory connectionFactory;
@Autowired
private DirectReceiver directReceiver;//Direct消息接收处理类
//@Autowired
//FanoutReceiverA fanoutReceiverA;//Fanout消息接收处理类A
@Autowired
DirectRabbitConfig directRabbitConfig;
//@Autowired
//FanoutRabbitConfig fanoutRabbitConfig;
@Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setConcurrentConsumers(1);
container.setMaxConcurrentConsumers(1);
// RabbitMQ默认是自动确认,这里改为手动确认消息
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
container.setQueues(directRabbitConfig.TestDirectQueue());
container.setMessageListener(directReceiver);
//container.addQueues(fanoutRabbitConfig.queueA());
//container.setMessageListener(fanoutReceiverA);
return container;
}
}
然后在直连型交换机的消息接收处理类上需要添加相关的消息手动确认代码DirectReceiver.java:
@Component
@RabbitListener(queues = "TestDirectQueue")//监听的队列名称 TestDirectQueue
public class DirectReceiver implements ChannelAwareMessageListener {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
//因为传递消息的时候用的map传递,所以将Map从Message内取出需要做些处理
System.out.println("TestDirectQueue:"+message.toString());
channel.basicAck(deliveryTag, true);
//为true会重新放回队列
//channel.basicReject(deliveryTag, true);
} catch (Exception e) {
channel.basicReject(deliveryTag, false);
e.printStackTrace();
}
}
}
调用下/sendDirectMessage接口往直连型交换机推送一条消息,看看监听到的消息结果:
TestDirectQueue:(Body:'{createTime=2020-04-06 23:53:41, messageId=61244683-3ec1-4bac-82e0-c47b83514130, messageData=124578}' MessageProperties [headers={spring_listener_return_correlation=24acf420-a4a4-4852-b299-3de6ab7ad03d}, contentType=application/x-java-serialized-object, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, redelivered=false, receivedExchange=TestDirectExchange, receivedRoutingKey=TestDirectRouting, deliveryTag=3, consumerTag=amq.ctag-xHTWQhB_p31ZyeleI0hsqA, consumerQueue=TestDirectQueue])