rabbitmq 重複ACK致使消息丟失

rabbitmq 重複確認致使消息丟失

背景

rabbitmq 在應用場景中,大多采用工做隊列 work-queue的模式。html

在一個常見的工做隊列模式中,消費者 worker 將不斷的輪詢從隊列中拉取最新消息,當隊列負載壓力增大時容許添加多個worker 進行處理。
然而執行一個任務可能須要至關的時長,這是由業務特性所決定的;若是 worker執行任務過程當中出現異常甚至宕機,此時消息便會丟失,這是簡單消息隊列難以解決的問題。網絡

rabbitmq 採用了消息確認機制來防止此類問題,在該機制中,worker須要向 MQ Server 返回 ACK響應以表示消息已確認處理;
在如下狀況下,rabbitmq 會對消息進行從新投遞:
1 client 未響應ACK, 主動關閉 Channel;
2 client 未響應ACk, 網絡異常斷開;測試

消息的重發機制沒有超時限制,只要client 不響應ACK,那麼會一直投遞;
若是啓用了消息持久化機制,那麼消息將有進一步的保障。this

問題描述及分析

1 客戶端爲簡化應答處理,能夠設置自動應答選項,如:編碼

boolean autoAck = false; channel.basicConsume(TASK_QUEUE_NAME, autoAck, consumer);

2 若是不啓用自動應答,須要應用代碼手動進行應答:spa

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日誌發現如下異常:code

{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次!server

通過分析,發現問題緣由所在:
rabbitmq 爲每個channel維護了一個delivery tag的計數器,這裏採用正向自增,新消息投遞時自增,當消息響應時自減;
在連續收發的場景中,因爲消息發送的間隔較短,部分消息因 consumer的重複確認被rabbitmq 當作已處理而丟棄。htm

解決方案

取消consumer 的自動應答機制,僅保留手動應答的處理,問題解決。

參考資料

關於 rabbitmq 消息確認機制:
http://www.rabbitmq.com/confirms.html#when

相關文章
相關標籤/搜索