聊聊MQ,如何避免訊息丟失?如何避免重複消費?
theme: vuepress
一起養成寫作習慣!這是我參與「掘金日新計劃 · 4 月更文挑戰」的第5天,點選檢視活動詳情。
前言
我在工作中,使用到訊息中介軟體MQ的業務還是挺多的,我從事在一家交通行業的公司,業務中經常會涉及處理一些違法資料的場景,專案中經常會使用到RabbitMQ,今天想跟大家聊聊怎樣避免訊息丟失和重複消費是問題。
在我們發生訊息的時候,如果MQ伺服器突然宕機了會出現什麼情況?是不是我們發過去的訊息全部沒有了嗎?
是的,一般MQ中介軟體為了提高系統的吞吐量會把訊息儲存在記憶體中,如果不作其他處理,MQ伺服器一旦宕機,訊息將全部丟失。這個是業務不允許的,造成很大的影響。
在遇到這種問題的時候,一般有以下幾種處理方式。
如何避免訊息丟失
持久化
RabbitMQ中發訊息的時候會有個durable引數可以設定,設定為true,就會持久化。
這樣的話MQ伺服器即使宕機,重啟後磁碟檔案中有訊息的儲存,這樣就不會丟失了吧。是的這樣就一定概率的保障了訊息不丟失。
但還會有個場景,就是訊息剛剛儲存到MQ記憶體中,但還沒有來得及更新到磁碟檔案中,突然宕機了。這個場景在持續的大量訊息投遞的過程中,會很常見。
那怎麼辦?我們如何作才能保障一定會持久化到磁碟上面呢?
confirm機制
RabbitMQ利用confirm機制來通知我們是否持久化成功?
confirm機制的原理:
(1)訊息生產者把訊息傳送給MQ,如果接收成功,MQ會返回一個ack訊息給生產者;
(2)如果訊息接收不成功,MQ會返回一個nack訊息給生產者;
這樣是不是就可以保障100%訊息不丟失了呢?
如果我們生產者每發一條訊息,都要MQ持久化到磁碟中,然後再發起ack或nack的回撥。這樣的話是不是我們MQ的吞吐量很不高,因為每次都要把訊息持久化到磁碟中。寫入磁碟這個動作是很慢的。這個在高併發場景下是不能夠接受的,吞吐量太低了。
所以MQ持久化磁碟真實的實現,是通過非同步呼叫處理的,他是有一定的機制,如:等到有幾千條訊息的時候,會一次性的刷盤到磁碟上面。而不是每來一條訊息,就刷盤一次。
所以comfirm機制其實是一個非同步監聽的機制,是為了保證系統的高吞吐量,這樣就導致了還是不能夠100%保障訊息不丟失,因為即使加上了confirm機制,訊息在MQ記憶體中還沒有刷盤到磁碟就宕機了,還是沒法處理。
資料庫事務機制
生產者在投遞訊息之前,可以在本地資料庫建一張訊息表,先把訊息持久化到Redis或DB中,這樣就可以利用本地資料庫的事務機制。事務提交成功後,將訊息表中的訊息轉移到訊息佇列中。
confirm機制監聽訊息是否傳送成功?如ack成功訊息,刪除DB中此訊息。
如果nack不成功的訊息,這個可以根據自身的業務選擇是否重發此訊息。也可以刪除此訊息,由自己的業務決定。
如何避免訊息重複消費
冪等性也就是相同條件下對一個業務的操作,不管操作多少次,結果都是一樣。
重複消費的問題?
導致重複消費的原因可能出現在生產者,也可能出現在 MQ 或 消費者。
這裡說的重複消費問題是指同一個資料被執行了兩次,不單單指 MQ 中一條訊息被消費了兩次,也可能是 MQ 中存在兩條一模一樣的消費。
- 生產者:生產者可能會重複推送一條資料到 MQ 中,為什麼會出現這種情況呢?也許是一個 Controller 介面被重複呼叫了 2 次,沒有做介面冪等性導致的;也可能是推送訊息到 MQ 時響應比較慢,生產者的重試機制導致再次推送了一次訊息。
- MQ:在消費者消費完一條資料響應 ack 訊號消費成功時,MQ 突然掛了,導致 MQ 以為消費者還未消費該條資料,MQ 恢復後再次推送了該條訊息,導致了重複消費。
- 消費者:消費者已經消費完了一條訊息,正準備但是還未給 MQ 傳送 ack 訊號時,此時消費者掛了,服務重啟後 MQ 以為消費者還沒有消費該訊息,再次推送了該條訊息。
如何保證冪等性?
消費者怎麼解決重複消費問題呢?這裡提供兩種方法:
- 狀態判斷法:消費者消費資料後把消費資料記錄在 redis 中,下次消費時先到 redis 中檢視是否存在該訊息,存在則表示訊息已經消費過,直接丟棄訊息。
- 業務判斷法:通常資料消費後都需要插入到資料庫中,使用資料庫的唯一性約束防止重複消費。每次消費直接嘗試插入資料,如果提示唯一性欄位重複,則直接丟失訊息。一般都是通過這個業務判斷的方法就可以簡單高效地避免訊息的重複處理了。
- 一文弄懂Java中執行緒池原理
- 聊聊工作中,如何提升自己的程式設計能力?
- 聊聊MQ,如何避免訊息丟失?如何避免重複消費?
- Java實現串列埠通訊
- SpringBoot為什麼可以使用Jar包啟動?
- 2021,平凡的一年!
- 聊聊 Java 中引數傳遞的原理!
- 聊聊Pulsar,一款非常優秀的訊息中介軟體!!!
- 乾貨,聊聊Java中String類!!!
- 萬字!Java相關的學習資料整理(乾貨滿滿)
- 使用 ArrayList 應當避免的坑
- Java中避免空指標的幾個方法!
- 聊聊Jhipster,強烈推薦Java開發看看,節省很多時間!!!
- 給程式設計師新手的一些建議
- 計算機基礎知識:磁碟分割槽
- Java中的序列化
- Java中的控制流程語句詳解
- 程式設計師必備:Git入門,超詳細
- netty系列:使用 SSL/TLS 加密 Netty 程式
- 聊聊MySQL儲存引擎中索引如何落地?