消费消息确认
从安全角度考虑,网络是不可靠的,消费者是有可能在处理消息的时候失败。而我们总是希望我们的消息不能因为处理失败而丢失,基于此原因,rabbitmq提供了一个消息确认(message acknowledgements) 的概念:当一个消息从队列中投递给消费者(consumer)后,消费者会通知一下消息中间件(rabbitmq),这个可以是系统自动autoACK的也可以由处理消息的应用操作。
当 “消息确认” 被启用的时候,rabbitmq不会完全将消息从队列中删除,直到它收到来自消费者的确认回执(acknowledgement)。
为了解决这个问题,rabbitmq提供了2种处理模式来解决这个问题:
-
自动确认模式(automatic acknowledgement model):当RabbbitMQ将消息发送给应用后,消费者端自动回送一个确认消息。(使用AMQP方法:basic.deliver或basic.get-ok)。
-
显式确认模式(explicit acknowledgement model):RabbbitMQ不会完全将消息从队列中删除,直到消费者发送一个确认回执(acknowledgement)后再删除消息。(使用AMQP方法:basic.ack)。
显式确认模式
在显式确认模式下,消费者可以自由选择什么时候发送确认回执(acknowledgement)。消费者可以在收到消息后立即发送,或将未处理的消息存储后发送,或等到消息被处理完毕后再发送确认回执。
如果一个消费者在尚未发送确认回执的情况下挂掉了,那rabbitmq会将消息重新投递给另一个消费者。如果当时没有可用的消费者了,消息代理会死等下一个注册到此队列的消费者,然后再次尝试投递。
消费者在获取队列消息时,可以指定autoAck参数,采用显式确认模式,需要指定autoAck = flase
,在显式确认模式,RabbitMQ不会为未ack的消息设置超时时间,它判断此消息是否需要重新投递给消费者的唯一依据是消费该消息的消费者连接是否已经断开。如果断开连接,RabbitMQ也没有收到ACK,则Rabbit MQ会安排该消息重新进入队列,等待投递给下一个消费者。
在显式确认模式,确认回执的案例如下:
//设置非自动回执
boolean autoAck = false;
Channel finalChannel = channel;
try {
channel.basicConsume("test", autoAck, "test-consumer-tag",
new DefaultConsumer(finalChannel) {
@Override
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body)
throws IOException
{
//发布的每一条消息都会获得一个唯一的deliveryTag,deliveryTag在channel范围内是唯一的
long deliveryTag = envelope.getDeliveryTag();
//第二个参数是批量确认标志。如果值为true,则执行批量确认,此deliveryTag之前收到的消息全部进行确认; 如果值为false,则只对当前收到的消息进行确认
finalChannel.basicAck(deliveryTag, true);
}
}