ack機制
ack
分爲自動ack和手動ack兩種
如果是自動ack,有兩個弊端:
- MQ broker只需要確認消息發送成功,無需等待應答就會丟棄消息,這樣導致如果消費者客戶端還未處理完消息,出現異常或者斷電時消息丟失的後果。
- 自動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,最後再另外統一處理這些消費失敗的消息