電商場景下,如何處理消費過程中的重複訊息?

2022-09-22 00:06:20 字數 2976 閱讀 3371

摘要:比如乙個消費訂單訊息,統計下單金額的微服務。若不正確處理重複訊息,就會出現重複統計。那僅靠mq能保證訊息不重複嗎?
訊息傳遞過程中若失敗,則傳送方會執行重試,重試就可能產生重複訊息。若不處理重複訊息,可能收穫驚喜。比如乙個消費訂單訊息,統計下單金額的微服務。若不正確處理重複訊息,就會出現重複統計。那僅靠mq能保證訊息不重複嗎?

訊息重複必然存在,在mqtt協議,給出三種傳遞訊息時能夠提供的

服務質量從低到高:

至多一次。訊息在傳遞時,最多被送達一次。即沒什麼訊息可靠性保證,允許丟訊息。一般都是一些對訊息可靠性要求不太高的監控場景使用,比如每分鐘上報一次機房溫度資料,可接受資料少量丟失

至少一次。訊息在傳遞時,至少會被送達一次。即不允許丟訊息,但允許少量重複訊息

恰好一次。訊息在傳遞時,只會被送達一次,不允許丟失也不允許重複

服務質量標準不僅適於mqtt,對所有mq都適用。大部分mq提供服務質量都是at least once,如rocketmq、rabbitmq和kafka。可以說mq本身並不保證訊息不重複。

沒錯,kafka的確支援exactly once,但本文說的也沒問題。kafka的「exactly once」和訊息傳遞服務質量標準中的「exactly once」不同,它是kafka提供的另一特性,kafka中支援的事務也和通常理解的事務有差異。kafka中的事務和excactly once主要為配合流計算。

既然mq無法保證訊息不重複,就得消費**接受「訊息可能重複」這個現實,通過業務**解決重複訊息對業務的影響。

一般解決重複訊息方案就是在消費端,讓消費訊息的操作具備冪等性(idempotence):

描述乙個操作、方法或者服務,其任意多次執行所產生的影響均與一次執行的影響相同。

乙個冪等的方法,使用同樣引數,對它進行多次呼叫和一次呼叫,對系統產生影響一樣。所以,對冪等方法,無需擔心重複執行會改變系統。

不考慮併發,「將賬戶x的餘額設為100元」,執行一次後對系統的影響是,賬戶x的餘額變成了100元。只要提供引數100元不變,執行多少次,賬戶x餘額始終100,這操作就是個冪等操作。

「將賬戶x餘額加100元」,這操作就不是冪等,每執行次,賬戶餘額增加100,執行多次和執行一次對系統的影響(即賬戶餘額)不同。

若系統消費訊息的業務邏輯具冪等性,那就不用擔心訊息重複,因為同一訊息,消費一次和多次對系統影響一樣。即消費多次等於消費一次。

從對系統影響結果:at least once + 冪等消費 = exactly once。

最好從業務邏輯入手,將消費業務設計成具備冪等性的操作。但也不是所有業務都天然冪等,需要一些技巧。

比如對於:將賬戶x餘額加100。

可限制對每個轉賬單,每個賬戶只能執行一次變更操作。最簡單的,在db中建一張【轉賬流水表】:

然後給【轉賬單id,賬戶id】聯合起來建立唯一約束,這樣相同轉賬單id、賬戶id,表裡至多只存在一條記錄。

消費訊息邏輯可變為:「在【轉賬流水表】增加一條轉賬記錄,再根據轉賬記錄,非同步更新使用者餘額。」

在轉賬流水表加條轉賬記錄操作中,由於【轉賬單id,賬戶id】唯一約束,對同一轉賬單,同一賬戶只能插一條記錄,後續重複插入操作都會失敗,這就實現了冪等。

所以,只要是支援類似「insert if not exist」語義的儲存系統都可實現冪等。

比如,可用

替代資料庫中的唯一約束,實現冪等消費。

給資料變更設定乙個前置條件:

更新資料時,同時變更前置條件中需要判斷的資料。於是,重複執行該操作時,由於第一次更新資料時,已變更前置條件中的判斷資料,不滿足前置條件,則不會再執行更新。

「將賬戶x的餘額增加100元」,這操作加個前置條件,變為:「若賬戶x當前餘額為500元,將餘額加100元」就具備冪等性。對應到mq訊息,在訊息體中帶上當前餘額,消費時判斷db中當前餘額==訊息中的餘額,相等時才執行更新。

但要更新資料不是數值,或要做個複雜的更新操作咋辦?前置判斷條件是啥呢?

更通用的,是給資料增加版本號version屬性,每次更新資料前,比較

當前資料version == 訊息中的version

若前兩種方案都不適用,還有通用性最強、適用範圍最廣方案:記錄並檢查操作,也稱「token機制或guid(全域性唯一id)機制」,執行資料更新操作前,先檢查是否執行過這更新操作。

但分布式系統下很難實現:

比如對於同一訊息:「全域性id為8,操作為:給id為666賬戶增加100元」,可能出現這樣情況:

對此,可以用事務實現,也可以鎖,但在分布式系統下,分布式事務、分布式鎖都會引入高複雜度。所以一般不推薦。

這些冪等方案不僅可用於解決重複訊息問題,也可解決重複請求或重複呼叫問題。比如:

將乙個微服務設計成冪等的,解決rpc框架自動重試導致的重複呼叫問題

若mq實現exactly once,會引發:

所以,mq不實現exactly once,而是at least once + 冪等性,而冪等性我們消費端業務**自己處理。

mq即使做到exactly once級別,con也要做冪等。因為con從mq取訊息時,若con消費成功,但ack失敗,con還是會取到重複訊息,所以mq費力做成exactly once無法避免業務側訊息重複問題。

一般也不會有問題,因為使用我們的方法,一條具體訊息,總會落到確定的庫表,其重複訊息也會落地同樣庫表。

有的mq會有個特殊佇列,儲存這些總是消費失敗的「壞訊息」,然後繼續消費之後的訊息,避免這些壞訊息卡死佇列。這種壞訊息一般不會是因為網路原因或消費者宕機導致的,大多都是因為訊息資料本身有問題,消費者的業務邏輯無法處理。

exactly once,實現有效能損耗,併發高時易出現訊息堆積;訊息佇列設計初衷是解決解耦,而解耦的物件往往是高併發,對效能要求較高的,從產品需求層面講,訊息佇列設計更注重效能,而非精準(exactly once);基礎架構角度來說,關注點是佔比大的需求(不能不發,可以重發),佔比極小的需求(敏感型,只能觸發一次)可以單獨抽出來另外實現。最後,請教老師有沒有比較具體的業務場景,非用這種exactly once不可的。

點選關注,第一時間了解華為雲新鮮技術~

儲存過程中如何處理分頁

儲存過程中處理分頁的方法如下 1。利用not in和select top分頁 select top頁大小 from t表where id notin select top頁大小 頁數 id from 表order byid order byid 2。利用id大於多少和select top分頁 sel...

處理消費過程中的重複訊息

在訊息傳遞過程中,如果出現傳遞失敗的情況,傳送方會執行重試,重試過程中就有可能產生重複的訊息。如果沒有對重複訊息進行處理,就可能導致系統的資料出現錯誤。比如,乙個消費訂單訊息,統計下單金額的微服務,如果沒有正確處理重複訊息,那就會出現重複統計,導致統計結果錯誤。一 訊息重複的情況必然存在 在mqtt...

資料清洗中異常值如何處理(下)

第一我們給大家介紹的是基於模型檢測,具體操作就是先建立乙個資料模型,異常是那些同模型不能完美擬合的物件 如果模型是簇的集合,則異常是不顯著屬於任何簇的物件 在使用回歸模型時,異常是相對遠離 值的物件。而這個方法的優點就是有堅實的統計學理論基礎,當存在充分的資料和所用的檢驗型別的知識時,這些檢驗可能非...