最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

分布式技術(shù)原理與實戰(zhàn)45講--第29講:消息冪等:如何保證消息不被重復消費

2023-02-22 20:49 作者:gzqhero  | 我要投稿

應用的冪等是在分布式系統(tǒng)設(shè)計時必須要考慮的一個方面,如果對冪等沒有額外的考慮,那么在消息失敗重新投遞,或者遠程服務(wù)重試時,可能會出現(xiàn)許多詭異的問題。這一課時一起來看一下,在消息隊列應用中,如何處理因為重復投遞等原因?qū)е碌膬绲葐栴}。

對業(yè)務(wù)冪等的理解

首先明確一下, 冪等并不是問題,而是業(yè)務(wù)的一個特性。冪等問題體現(xiàn)在對于不滿足冪等性的業(yè)務(wù),在消息重復消費,或者遠程服務(wù)調(diào)用失敗重試時,出現(xiàn)的數(shù)據(jù)不一致,業(yè)務(wù)數(shù)據(jù)錯亂等現(xiàn)象。

冪等最早是一個數(shù)學上的概念,冪等函數(shù)指的是對一個函數(shù)或者方法,使用相同的參數(shù)執(zhí)行多次,數(shù)據(jù)結(jié)果是一致的。

以 HTTP 協(xié)議為例,我們知道 HTTP 協(xié)議中定義了交互的不同方法,比如 GET 和 POST,以及 PUT、DELETE 等,其中 GET、DELETE 等方法都是冪等的,而 POST 方法不是。

這個很好理解,GET 方法用于獲取資源,不管調(diào)用多少次接口,結(jié)果都不會改變,所以是冪等的,DELETE 等可以類比。

這里有一點需要注意,業(yè)務(wù)上的冪等指的是操作不影響資源本身,并不是每次讀取的結(jié)果都保證一致。比如通過 GET 接口查詢一條訂單記錄,在多次查詢的時間段內(nèi),訂單狀態(tài)可能會有新的更新而發(fā)生變化,查詢到的數(shù)據(jù)可能不同,但是讀接口本身仍然是一個冪等的操作。

在業(yè)務(wù)開發(fā)中對數(shù)據(jù)的操作主要是 CRUD,即在做數(shù)據(jù)處理時的 Create、Read、Update、Delete 這幾種操作。很明顯,這里的 Create 操作不是冪等的,Update 操作可能冪等也可能不冪等。例如,現(xiàn)在有一個訂單表,下面的操作就是冪等的:

UPDATE order SET status=1 WHERE id=100

下面的這個操作,就不符合冪等性的要求:

UPDATE order SET price=price+1 WHERE id=100

對應的,Read 和 Delete 操作則是冪等的。

各類中間件對冪等性的處理

冪等處理不好,可能會出現(xiàn)很多問題,比如使用 binlog 分發(fā)進行數(shù)據(jù)同步,如果數(shù)據(jù)庫更新消息被多次消費,可能會導致數(shù)據(jù)的不一致。

  • 遠程服務(wù)調(diào)用的冪等問題

因為存在網(wǎng)絡(luò)抖動等,遠程服務(wù)調(diào)用出現(xiàn)失敗,一般是通過配置重試,保證請求調(diào)用成功率,提高整體服務(wù)的可用性。

以 Apache Dubbo 為例,我一直覺得 Dubbo 對容錯的支持特別全面,它支持多種集群容錯的方式,并且可以針對業(yè)務(wù)特性,配置不同的失敗重試機制,包括 Failover 失敗自動切換、Failsafe 失敗安全、Failfast 快速失敗等。比如在 Failover 下,失敗會重試兩次;在 Failfast 下,失敗則不會重試,直接拋出異常。

Dubbo 的容錯機制考慮了多種業(yè)務(wù)場景的需求,根據(jù)不同的業(yè)務(wù)場景,可以選擇不同的容錯機制,進而有不同的重試策略,保證業(yè)務(wù)正確性。

Dubbo RPC 的重試和容錯機制不是本課時的重點,如果想對 Dubbo 集群容錯方式有進一步的了解,可以點擊查看 Dubbo 官方文檔。

  • 消息消費中的重試問題

從本質(zhì)上來講,消息隊列的消息發(fā)送重試,和微服務(wù)中的失敗調(diào)用重試是一樣的,都是通過重試的方式,解決網(wǎng)絡(luò)抖動、傳輸不穩(wěn)定等導致的偶發(fā)調(diào)用失敗。這兩者其實是一個問題,兩個問題的解決方式也可以互相借鑒。

在分布式系統(tǒng)中,要解決這個問題,需從中間件和業(yè)務(wù)的不同層面,來保證服務(wù)調(diào)用的冪等性。下面從消息隊列投遞語義,以及業(yè)務(wù)中如何處理冪等,兩個方面進行拆解。

消息投遞的幾種語義

為了進一步規(guī)范消息的調(diào)用,業(yè)界有許多消息隊列的應用協(xié)議,其中也對消息投遞標準做了一些約束。

  • At most once

消息在傳遞時,最多會被送達一次,在這種場景下,消息可能會丟,但絕不會重復傳輸,一般用于對消息可靠性沒有太高要求的場景,比如一些允許數(shù)據(jù)丟失的日志報表、監(jiān)控信息等。

  • At least once

消息在傳遞時,至少會被送達一次,在這種情況下,消息絕不會丟,但可能會出現(xiàn)重復傳輸。

絕大多數(shù)應用中,都是使用至少投遞一次這種方式,同時,大部分消息隊列都支持到這個級別,應用最廣泛。

  • Exactly once

每條消息肯定會被傳輸一次且僅傳輸一次,并且保證送達,因為涉及發(fā)送端和生產(chǎn)端的各種協(xié)同機制,絕對的?Exactly once 級別是很難實現(xiàn)的,通用的 Exactly once 方案幾乎不可能存在,可以參考分布式系統(tǒng)的「FLP 不可能定理」。

我覺得消息投遞的語義,和數(shù)據(jù)庫的隔離級別很像,不同語義的實現(xiàn),付出的成本也不一樣。上面定義的消息投遞語義,主要在消息發(fā)送端,在消費端也可以定義類似的消費語義,比如消費端保證最多被消費一次,至少被消費一次等,這兩種語義是相對應的,可以認為是同一個級別的兩種描述。

不同消息隊列支持的投遞方式

以 RocketMQ 為例,我們來看下對應的投遞支持。

RocketMQ 支持 At least once 的投遞語義,也就是保證每個消息至少被投遞一次。在 RocketMQ 中,是通過消費端消費的 ACK 機制來實現(xiàn)的:

在消息消費過程中,消費端在消息消費完成后,才返回 ACK,如果消息已經(jīng) pull 到本地,但還沒有消費,則不會返回 ack 響應。

在業(yè)務(wù)上應用 RcoketMQ 時,也可以根據(jù)不同的業(yè)務(wù)場景實現(xiàn)其他級別的投遞語義,比如最多送達一次等,由于篇幅限制這里不展開詳細講解了,感興趣的同學可以查閱 RocketMQ 相關(guān)的源碼和文檔學習。

業(yè)務(wù)上如何處理冪等

消息消費的冪等和我們在上一課時中提到的時序性一樣,本質(zhì)上也是一個系統(tǒng)設(shè)計的問題。

消息隊列是我們?yōu)榱藢崿F(xiàn)系統(tǒng)目標而引入的手段之一,并且分布式消息隊列天然存在消費時序、消息失敗重發(fā)等問題。所以要保證消息隊列的消費冪等,還是要回到業(yè)務(wù)中,結(jié)合具體的設(shè)計方案解決。

天然冪等不需要額外設(shè)計

參考上面對 HTTP 協(xié)議方法的冪等性分析,有部分業(yè)務(wù)是天然冪等的,這部分業(yè)務(wù),允許重復調(diào)用,即允許重試,在配置消息隊列時,還可以通過合理的重試,來提高請求的成功率。

利用數(shù)據(jù)庫進行去重

業(yè)務(wù)上的冪等操作可以添加一個過濾的數(shù)據(jù)庫,比如設(shè)置一個去重表,也可以在數(shù)據(jù)庫中通過唯一索引來去重。

舉一個例子,現(xiàn)在要根據(jù)訂單流轉(zhuǎn)的消息在數(shù)據(jù)庫中寫一張訂單 Log 表,我們可以把訂單 ID 和修改時間戳做一個唯一索引進行約束。

當消費端消費消息出現(xiàn)重復投遞時,會多次去訂單 Log 表中進行寫入,由于我們添加了唯一索引,除了第一條之外,后面的都會失敗,這就從業(yè)務(wù)上保證了冪等,即使消費多次,也不會影響最終的數(shù)據(jù)結(jié)果。

設(shè)置全局唯一消息?ID?或者任務(wù) ID

還記得我們在第 15 課時「分布式調(diào)用鏈跟蹤」中,提到的調(diào)用鏈 ID 嗎?調(diào)用鏈 ID 也可以應用在這里。我們在消息投遞時,給每條業(yè)務(wù)消息附加一個唯一的消息 ID,然后就可以在消費端利用類似分布式鎖的機制,實現(xiàn)唯一性的消費。

還是用上面記錄訂單狀態(tài)流轉(zhuǎn)消息的例子,我們在每條消息中添加一個唯一 ID,消息被消費后,在緩存中設(shè)置一個 Key 為對應的唯一 ID,代表數(shù)據(jù)已經(jīng)被消費,當其他的消費端去消費時,就可以根據(jù)這條記錄,來判斷是否已經(jīng)處理過。

總結(jié)

這一課時分享了消息冪等的知識點,包括對冪等的理解,以及消息隊列投遞時的不同語義,另外簡單介紹了業(yè)務(wù)上處理冪等的兩種方式。

西方有一句諺語:當你有了一個錘子,你看什么都像釘子。在我剛開始學習分布式系統(tǒng)時,學習了各種中間件,每個中間件都希望能用上,這其實脫離了系統(tǒng)設(shè)計的初衷。

課程內(nèi)容到這里,已經(jīng)展開了許多分布式系統(tǒng)的常用組件,提到這個諺語,主要是希望你在做技術(shù)方案,特別是做分布式系統(tǒng)設(shè)計方案時,不是為了設(shè)計而設(shè)計。方案設(shè)計的目的是 實現(xiàn)業(yè)務(wù)目標,并不是在系統(tǒng)中加入各種高大上的中間件,這個方案就是正確的。

我之前讀過一本《系統(tǒng)之美》的圖書,從復雜系統(tǒng)的角度來看,系統(tǒng)中的元素越多,為了維持系統(tǒng)的平衡,需要付出的勢能必然也越大。

對應到系統(tǒng)設(shè)計中,系統(tǒng)拆解的粒度越大,對應各個組件之間的耦合就越小,但是需要解決的組件協(xié)同問題也越多,實現(xiàn)數(shù)據(jù)的一致性也越困難。我們在系統(tǒng)設(shè)計時,要避免過度設(shè)計,把握技術(shù)方案的核心目的,在這個基礎(chǔ)上進行針對性設(shè)計。

對于這一課時的內(nèi)容,你可以思考下當前的項目中是如何處理重復消息的,有沒有考慮消息處理的冪等性?歡迎留言分享。


分布式技術(shù)原理與實戰(zhàn)45講--第29講:消息冪等:如何保證消息不被重復消費的評論 (共 條)

分享到微博請遵守國家法律
庆元县| 云林县| 师宗县| 伊吾县| 旅游| 潜山县| 保康县| 南汇区| 永昌县| 松江区| 昌邑市| 大冶市| 湘阴县| 丹江口市| 望谟县| 独山县| 涞水县| 和顺县| 芷江| 格尔木市| 鄯善县| 邵东县| 梁山县| 崇明县| 陆川县| 泰宁县| 扶余县| 大港区| 镶黄旗| 修文县| 孝感市| 正镶白旗| 竹北市| 武隆县| 潜山县| 广河县| 晴隆县| 临潭县| 关岭| 雅安市| 东宁县|