一、场景描述
rabbitmq经常会用到一个参数requeue,消费者在返回Nack时通过设置requeue=true,确保消息重新排队后继续被消费。
疑问:消息requeue底层是怎么实现的,猜测是以下两种情况之一:
1、消费者(Consumer)从本地队列中删除该消息并通知Rabbitmq将该消息从Broker维护的队列头部取出放到队列尾部?
2、消费者(Consumer)只是将该消息重新放到本地维护的队列尾部,Rabbitmq服务端队列无须任何操作?
带着以上两个问题,我们先通过一个简单的demo来验证一下
二、过程分析
测试的Demo代码是这样约定的:
数字0:消费者返回ACK,消费成功
数字1:首次消费1时模拟异常返回Nack,同时设置Requeue=true,第二次消费1时返回ACK
数字2:首次消费2时模拟异常返回Nack,同时设置Requeue=true,第二次消费2时返回ACK
数字3:消费者返回ACK,消费成功
数字4:消费者返回ACK,消费成功
通过代码debug,消费过程的示意图如下(基中标蓝色的是Unacked状态的message):
1、消费者暂且设置qos=5,即broker会向消费者一次性投递5条消息
2、Consumer消费完0后返回ACK,Broker会删除对应的消息0
3、继续消费1时,消费者发生了异常并且捕获异常后返回了Nack,且requeue=true
我们可以看到,消费者消息消费失败后尝试重新排队,此时消息1回到了消费者本地队列的尾部
4、继续消费2时,消费者发生了异常并且捕获异常后返回Nack,且requeue=true
此时消息2回到了本地队列的尾部
5、继续消费3后返回Ack,Consumer队列中的消息3被删除,其中Broker中的消息3被标记为'待删除',但还留在队列中,等待消息1、2依次出队列后被删除
6、继续消费4后返回Ack,Consumer队列中的消息4被删除,其中Broker中的消息4被标记为'待删除',但还留在队列中,等待消息1、2、3依次出队列后被删除
最终消费端 消费消息 的顺序打印如下:0 -> 3 -> 4 -> 1 -> 2
注意:如果本地队列中存在未消费完的消息(Unacked),那么RabbitMQ服务端不会继续投递消息
三、总结
返回Nack且requeue为true时,重新排队是在消费者端完成的,对Broker中的队列和消息没有影响