rabbitmq ack与nack导致的队列消息堵塞以及死循环问题

ack机制

ack分为自动ack和手动ack两种
如果是自动ack,有两个弊端:

  1. MQ broker只需要确认消息发送成功,无需等待应答就会丢弃消息,这样导致如果消费者客户端还未处理完消息,出现异常或者断电时消息丢失的后果。
  2. 自动ack没有qos控制,可能消费者客户端因为瞬间收到太多消息导致服务挂掉

所以,常用的是手动ack应答

手动ack

一般手动ack处理业务的逻辑如下:

try {
    //do logic   
    if(success) {
            //手动ack应答
            $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
        }
} catch (Exception $e) {
	echo "wrong";
	//log
}

咋一看是没问题,但如果上面logic中的callback有Bug的话,就会导致所有的消息都抛出异常,然后队列的Unacked消息数暴涨,导致MQ响应越来越慢,然后down掉
请输入图片描述

原因:因为上面callback抛出异常,所以MQ没有得到ack响应,注意:这些消息会堆积在Unacked消息里,不会抛弃,即使另外打开一个消费者也不会被消费,直到原来的消费者客户端断开重连时,才会变成ready,这时如果通过qos设置了prefetch,没有ack响应的话,Broker不会再分配新的消息下来,就导致了阻塞

nack机制

nack是什么呢?其实就是会通知MQ把消息塞回的队列头部(不是尾部),而不是变成Unacked,这样消费者客户端可以直接获取到这条消息。
但是问题又来了,如果上面callback有问题,那就算放回队首了,下次取出消费,还是会报错,又被送回队首,这样就陷入死循环

总结

解决上面出现的问题的最好的办法就是:

try {
    //do logic   
} catch (Exception $e) {
   Log.Info($msg)
} finally {
   //最终怎样都手动ack应答
   $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
}

也就是不管是否出现异常,都要ack,区别在与出现异常时先把消息数据catch到一个记录表里,然后ack,最后再另外统一处理这些消费失败的消息

发布了48 篇原创文章 · 获赞 56 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/zhetmdoubeizhanyong/article/details/103223777