RabbitMQ如何保證訊息的可靠性
RabbitMQ訊息丟失的三種情況
生產者弄丟訊息時的解決方法
- 方法一:生產者在傳送資料之前開啟RabbitMQ的事務(採用該種方法由於事務機制,會導致吞吐量下降,太消耗效能。)
- 方法二:開啟confirm模式(使用springboot時在application.yml配置檔案中做如下配置,實現confirm回撥介面,生產者傳送訊息時設定confirm回撥)
- 小結: 事務機制和 confirm機制最大的不同在於,事務機制是同步的,你提交一個事務之後會阻塞在那兒,但是 confirm機制是非同步的,你傳送個訊息之後就可以傳送下一個訊息,RabbitMQ 接收了之後會非同步回撥confirm介面通知你這個訊息接收到了。一般在生產者這塊避免資料丟失,建議使用用 confirm 機制。
MQ自身弄丟訊息時的解決方法
- 第一步: 建立queue時設定為持久化佇列,這樣可以保證RabbitMQ持久化queue的後設資料,此時還是不會持久化queue裡的資料。
- 第二步: 傳送訊息時將訊息的deliveryMode設定為持久化,此時queue中的訊息才會持久化到磁碟。
- 總結:同時設定queue和message持久化以後,RabbitMQ 掛了再次重啟,也會從磁碟上重啟恢復 queue,恢復這個 queue 裡的資料,保證資料不會丟失。
- 但是:但是就算開啟持久化機制,也有可能出現上面說的的訊息落盤時服務掛掉的情況。這時可以考慮結合生產者的confirm機制來處理,持久化機制開啟後訊息只有成功落盤時才會通過confirm回撥通知生產者,所以可以考慮生產者在生產訊息時維護一個正在等待訊息傳送確認的佇列,如果超過一定時間還沒從confirm中收到對應訊息的反饋,自動進行重發處理。
消費者自身弄丟訊息時的解決方法
- 方法:關閉自動ACK,使用手動ACK。RabbitMQ中有一個ACK機制,預設情況下消費者接收到到訊息,RabbitMQ會自動提交ACK,之後這條訊息就不會再傳送給消費者了。我們可以更改為手動ACK模式,每次處理完訊息之後,再手動ack一下。不過這樣可能會出現剛處理完還沒手動ack確認,消費者掛了,導致訊息重複消費,不過我們只需要保證冪等性就好了,重複消費也不會造成問題。
- 步驟一:在springboot中修改application.yml配置檔案更改為手動ack模式
- 步驟二:手動實現ack的callback
RabbitMQ保證訊息可靠性總結
RabbitMQ如何保證訊息的冪等性
如何保證訊息佇列消費的冪等性,這一塊應該還是要結合業務來選擇合適的方法,有以下幾個方案:
- 消費資料為了單純的寫入資料庫,可以先根據主鍵查詢資料是否已經存在,如果已經存在了就沒必要插入了。或者直接插入也沒問題,因為可以利用主鍵的唯一性來保證資料不會重複插入,重複插入只會報錯,但不會出現髒資料。
- 消費資料只是為了快取到redis當中,這種情況就是直接往redis中set value了,天然的冪等性。
- 針對複雜的業務情況,可以在生產訊息的時候給每個訊息加一個全域性唯一ID,消費者消費訊息時根據這個ID去redis當中查詢之前是否消費過。如果沒有消費過,就進行消費並將這個訊息的ID寫入到redis當中。如果已經消費過了,就無需再次消費了。
RabbitMQ如何保證訊息的順序性
出現消費順序錯亂的情況
- 為了提高處理效率,一個queue存在多個consumer
- 一個queue只存在一個consumer,但是為了提高處理效率,consumer中使用了多執行緒進行處理
保證訊息順序性的方法
- 將原來的一個queue拆分成多個queue,每個queue都有一個自己的consumer。該種方案的核心是生產者在投遞訊息的時候根據業務資料關鍵值(例如訂單ID雜湊值對訂單佇列數取模)來將需要保證先後順序的同一類資料(同一個訂單的資料) 傳送到同一個queue當中。
- 一個queue就一個consumer,在consumer中維護多個記憶體佇列,根據業務資料關鍵值(例如訂單ID雜湊值對記憶體佇列數取模)將訊息加入到不同的記憶體佇列中,然後多個真正負責處理訊息的執行緒去各自對應的記憶體佇列當中獲取訊息進行消費。
RabbitMQ保證訊息順序性總結:
核心思路就是根據業務資料關鍵值劃分成多個訊息集合,而且每個訊息集合中的訊息資料都是有序的,每個訊息集合有自己獨立的一個consumer。多個訊息集合的存在保證了訊息消費的效率,每個有序的訊息集合對應單個的consumer也保證了訊息消費時的有序性。