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,最後再另外統一處理這些消費失敗的消息

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章