rabbitmq 重複确認導緻消息丢失
背景
rabbitmq 在應用場景中,大多采用工作隊列 work-queue的模式。
在一個常見的工作隊列模式中,消費者 worker 将不斷的輪詢從隊列中拉取最新消息,當隊列負載壓力增大時允許添加多個worker 進行處理。
然而執行一個任務可能需要相當的時長,這是由業務特性所決定的;如果 worker執行任務過程中出現異常甚至當機,此時消息便會丢失,這是簡單消息隊列難以解決的問題。
rabbitmq 采用了消息确認機制來防止此類問題,在該機制中,worker需要向 MQ Server 傳回 ACK響應以表示消息已确認處理;
在以下情況下,rabbitmq 會對消息進行重新投遞:
1 client 未響應ACK, 主動關閉 Channel;
2 client 未響應ACk, 網絡異常斷開;
消息的重發機制沒有逾時限制,隻要client 不響應ACK,那麼會一直投遞;
如果啟用了消息持久化機制,那麼消息将有進一步的保障。
問題描述及分析
1 用戶端為簡化應答處理,可以設定自動應答選項,如:
boolean autoAck = false;
channel.basicConsume(TASK_QUEUE_NAME, autoAck, consumer);
2 如果不啟用自動應答,需要應用代碼手動進行應答:
try {
doWork(message);
} finally {
logger.info(" xxx work done");
channel.basicAck(envelope.getDeliveryTag(), false);
}
3 當兩種方案同時存在
由于用戶端的編碼失誤,先啟用了自動應答選項,又在應用代碼執行了應答的代碼:
// enable autoAck
boolean autoAck = true;
consumerChannel.basicConsume(queueName, autoAck, this);
//...
// snipper from Consumer.handleDelivery method
// send ack to server
try {
consumerChannel.basicAck(deliveryTag, true);
} catch (Exception e) {
}
多了一次确認,應用代碼貌似一切如常。 但在頻繁進行消息收發測試時發現 消息存在随機性丢失處理的情況!
檢查 rabbitmq server日志發現以下異常:
{amqp_error,precondition_failed,"unknown delivery tag 1",'basic.ack'}
...
{amqp_error,precondition_failed,"unknown delivery tag 1",'basic.ack'}
...
{amqp_error,precondition_failed,"unknown delivery tag 1",'basic.ack'}
...
提示未知的 delivery tag=1,該字段為MQ server 用于消息确認的标記,服務端因無法識别而列印錯誤。
另外一個現象則是,連續收發消息 5次,其中丢失消息處理1次,而 rabbitmq server錯誤日志出現 4次!
經過分析,發現問題原因所在:
rabbitmq 為每一個channel維護了一個delivery tag的計數器,這裡采用正向自增,新消息投遞時自增,當消息響應時自減;
在連續收發的場景中,由于消息發送的間隔較短,部分消息因 consumer的重複确認被rabbitmq 當做已處理而丢棄。
解決方案
取消consumer 的自動應答機制,僅保留手動應答的處理,問題解決。
參考資料
關于 rabbitmq 消息确認機制:
http://www.rabbitmq.com/confirms.html#when
作者:
zale出處:
http://www.cnblogs.com/littleatp/, 如果喜歡我的文章,請
關注我的公衆号本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出
原文連結如有問題, 可留言咨詢.