回覆列表
-
1 # 會點程式碼的大叔
-
2 # 小馬過河Vizit
如何保證不重複消費?
不同的訊息系統會有不同的方案。比如Kafka。Kafka的每條訊息都有一個offset。如果你消費Kafka的訊息之後儲存一下自己消費成功的offset,那麼下次消費的時候從上次消費的offset開始就可以避免重複。我有個動畫影片講解了這個原理,歡迎觀看。
如何保證冪等?
當重複消費同一條訊息的時候,如果不做冪等處理,就會產生重複的結果。可以這麼處理:
存在性檢查,可以檢查這條訊息是否被消費過;可以透過記錄消費過的offset,來去重。
也可以檢查這條訊息產生的結果是否已經存在了;如果消費的結果的key是由訊息裡面的一些資訊生成出來的唯一值,那麼可以利用這個key來去重。
今天,讓我們一起看看保證 MQ 的冪等性有哪些方案。
01. 冪等性的概念先說說什麼是冪等性。
當用戶對同一操作請求了一次或者多次(訊息傳送或接收了多次),最終的結果是一致的,並不會因為多次請求產生副作用;比如同一個訂單支付了兩次,最後應該只扣客戶一次錢。
新增和修改:如果不做冪等性處理,可能就會產生問題;執行多次新增操作,可能會導致一模一樣的資料產生了多條(主鍵自動生成);修改操作,如果只是把某些欄位更新成固定的值,不會有冪等性問題,但是如果新值要在舊值上做處理做計算,如增加多少、減少多少,那麼多次執行的結果就會有差異。
02. MQ 訊息出現非冪等的情況1. 生產者傳送訊息給 MQ,為了保證訊息可達,通常會使用超時重傳機制,但是如果生產者的訊息已經發出去,但是由於網路原因未收到確認資訊,那麼可能會進行重發,最終造成了訊息的重複傳送。
2. 消費者消費的過程也類似,消費者接受到 MQ 的訊息,但是 MQ 未收到確認資訊,那麼該條訊息可能會重新發送給其他消費者,或者網路恢復再次傳送給消費者,最終造成重複消費。
03. 解決方法1. 唯一索引
使用唯一索引,可以有效的防止新增髒資料:當表中存在唯一索引的時候,併發新增相同資料的時候就會報錯,不過這在單庫單表的時候才有效,如果專案資料量很大,採用了分庫分表的策略,就不能再透過資料庫的唯一性索引來解決冪等性的問題了。
2. 全域性唯一 ID
每條訊息,都攜帶一個全域性的唯一 ID,消費者接收到訊息,在執行前判斷這個 ID 是否已經在本地存在,如果不存在,則執行交易後記錄 ID(存到資料庫或Redis中,表示該交易已經執行);如果已經存在,表示訊息已經消費掉了,不能再次執行。
許多分散式架構中,生成全域性唯一 ID 都會被作為一個基礎的微服務,當然這個服務的可靠性要求極高,或者可以使用全域性唯一 ID 演算法,由每個應用自己生成。不過總的來說,引入全域性唯一 ID 這個方案,實現起來還是非常繁瑣的。
3. 業務狀態
保證訊息的冪等性,也可以結合業務流程考慮,比如很多業務流程每一步都是有狀態的,比如網上購物可能會有:訂單建立、付款、發貨,那麼付款之前保單狀態為“待付款”,付款之後可以將保單的狀態修改為“待發貨”;那麼如果發起重複扣款的話,第二次扣款的時候保單狀態已經變化了,就會扣款失敗。
4. 去重表
如果業務中有唯一性的標識時,可以使用去重表,把這個唯一性的表示儲存到去重表中,如果重複插入,那麼會被校驗住。
比如上面的場景,一個訂單隻會付款一次,那麼在付款的時候,把訂單號作為唯一性的標識,儲存到去重表中,可以保證付款操作只會發生一次;這個方法也用到了唯一 ID,不過和全域性唯一 ID 不同,這個唯一 ID 是針對具體業務的。