rabbitmq 重複ACK導致訊息丟失

2021-09-20 09:12:48 字數 1686 閱讀 9527

背景

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  finally
3 當兩種方案同時存在

由於客戶端的編碼失誤,先啟用了自動應答選項,又在應用**執行了應答的**:
// enable autoack

boolean autoack = true;

consumerchannel.basicconsume(queuename, autoack, this);

//...

// snipper from consumer.handledelivery method

// send ack to server

try catch (exception e)

多了一次確認,應用**貌似一切如常。 但在頻繁進行訊息收發測試時發現 訊息存在隨機性丟失處理的情況!

檢查 rabbitmq server日誌發現以下異常:

...

......

提示未知的 delivery tag=1,該字段為mq server 用於訊息確認的標記,服務端因無法識別而列印錯誤。

另外乙個現象則是,連續收發訊息 5次,其中丟失訊息處理1次,而 rabbitmq server錯誤日誌出現 4次!

經過分析,發現問題原因所在:

rabbitmq 為每乙個channel維護了乙個delivery tag的計數器,這裡採用正向自增,新訊息投遞時自增,當訊息響應時自減;

在連續收發的場景中,由於訊息傳送的間隔較短,部分訊息因 consumer的重複確認被rabbitmq 當做已處理而丟棄。

解決方案

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

參考資料

關於 rabbitmq 訊息確認機制:

RabbitMQ訊息佇列 ACK機制

如果乙個consumer異常退出了,它處理的資料能夠被另外的consumer處理,這樣資料在這種情況下就不會丟失了 注意是這種情況下 為了保證資料不被丟失,rabbitmq支援訊息確認機制,即acknowledgments。為了保證資料能被正確處理而不僅僅是被consumer收到,那麼我們不能採用n...

RabbitMQ訊息應答 ack機制

執行乙個任務可能需要花費幾秒鐘,你可能會擔心如果乙個消費者在執行任務過程中掛掉了。一旦rabbitmq將訊息分發給了消費者,就會從記憶體中刪除。在這種情況下,如果正在執行任務的消費者宕機,會丟失正在處理的訊息和分發給這個消費者但尚未處理的訊息。但是,我們不想丟失任何任務,如果有乙個消費者掛掉了,那麼...

RabbitMQ訊息佇列 ACK機制

如果乙個consumer異常退出了,它處理的資料能夠被另外的consumer處理,這樣資料在這種情況下就不會丟失了 注意是這種情況下 為了保證資料不被丟失,rabbitmq支援訊息確認機制,即acknowledgments。為了保證資料能被正確處理而不僅僅是被consumer收到,那麼我們不能採用n...