這篇文章主要講解了“分布式系統(tǒng)如何實(shí)現(xiàn)冪等性”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“分布式系統(tǒng)如何實(shí)現(xiàn)冪等性”吧!
成都創(chuàng)新互聯(lián)公司服務(wù)項(xiàng)目包括隆子網(wǎng)站建設(shè)、隆子網(wǎng)站制作、隆子網(wǎng)頁(yè)制作以及隆子網(wǎng)絡(luò)營(yíng)銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,隆子網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到隆子省份的部分城市,未來相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
案例一:轉(zhuǎn)賬系統(tǒng)
在之前的文章,有多次提到轉(zhuǎn)賬系統(tǒng)這個(gè)案例,由于這個(gè)案例太典型了,很多大學(xué)教授數(shù)據(jù)庫(kù)事務(wù)的時(shí)候就是用的這個(gè)案例。
對(duì)于一個(gè)單體應(yīng)用版的轉(zhuǎn)賬系統(tǒng),我們可以直接利用數(shù)據(jù)庫(kù)的事務(wù)來保證整個(gè)轉(zhuǎn)賬操作的ACID。但是,隨著用戶量級(jí)的增加,單個(gè)數(shù)據(jù)庫(kù)的瓶頸也隨之出現(xiàn),于是就出現(xiàn)了分庫(kù)分表的設(shè)計(jì),即:一部分用戶信息存儲(chǔ)在一個(gè)數(shù)據(jù)庫(kù),另一部分存儲(chǔ)在另一個(gè)數(shù)據(jù)庫(kù)。基于這樣的設(shè)計(jì),單個(gè)數(shù)據(jù)庫(kù)的事務(wù)肯定就不可用了,我們需要采用跨數(shù)據(jù)庫(kù)的分布式事務(wù),比如基于XA協(xié)議的分布式事務(wù),但是這種方式有一些自身的問題,并且有應(yīng)用場(chǎng)景的局限性。所以,一般來說實(shí)際場(chǎng)景都是采用基于BASE的最終一致性解決方案。
如下則是一個(gè)簡(jiǎn)單的最終一致性方案設(shè)計(jì):
Step 1:Application收到用戶發(fā)出的一個(gè)轉(zhuǎn)賬請(qǐng)求之后,首先執(zhí)行轉(zhuǎn)出方的邏輯,如下:
begin transaction記賬單 (包括:轉(zhuǎn)賬請(qǐng)求uuid+轉(zhuǎn)賬狀態(tài)in progress)扣錢(轉(zhuǎn)出方余額減少)commit/rollback
這段邏輯包含在一個(gè)transaction里面,由于只牽扯到一個(gè)數(shù)據(jù)庫(kù),可以利用單個(gè)數(shù)據(jù)庫(kù)的事務(wù)保證。
Step 2:一個(gè)background job不斷的抓取in progress的記賬單,然后發(fā)送event(通知收款方收錢)到Kafka,發(fā)送成功之后,把賬單狀態(tài)改成success。
這段邏輯就是outbox pattern的實(shí)現(xiàn),關(guān)于outbox pattern的具體介紹,可以參考我的另外一篇文章(空談發(fā)件箱模式(outbox pattern))。
Step 3:轉(zhuǎn)入方實(shí)現(xiàn)有個(gè)listener一直監(jiān)聽這個(gè)event,當(dāng)監(jiān)聽到這個(gè)event時(shí),執(zhí)行如下邏輯:
begin transaction記賬單(包括:轉(zhuǎn)賬請(qǐng)求uuid+轉(zhuǎn)賬狀態(tài)success)加錢(轉(zhuǎn)入方余額增加)commit/rollback
轉(zhuǎn)入方的邏輯處理也是在一個(gè)transaction里面,可以通過單個(gè)數(shù)據(jù)庫(kù)的事務(wù)保證。
但是,上面的設(shè)計(jì)可能有多個(gè)地方會(huì)出現(xiàn)event消息重發(fā)的情況,比如:background job發(fā)送event成功,但是修改賬單狀態(tài)失敗;或者,轉(zhuǎn)入方邏輯commit到數(shù)據(jù)庫(kù)成功,但是發(fā)送ack給Kafka出問題,等等。那么,如何處理這樣的重復(fù)消費(fèi)消息的情況呢?因?yàn)槿绻幚聿划?dāng),就可能會(huì)導(dǎo)致數(shù)據(jù)不一致。其實(shí),這本質(zhì)上就是一個(gè)冪等性問題,保證收到重復(fù)消息和收到一次消息的處理結(jié)果是一致的,就是冪等的。
對(duì)于上面的設(shè)計(jì),要保證冪等性,可以在賬單表中存一個(gè)request uuid,利用這個(gè)uuid達(dá)到去重的效果,具體是:轉(zhuǎn)入方在收到重復(fù)轉(zhuǎn)賬event消息時(shí),根據(jù)request uuid先去數(shù)據(jù)庫(kù)里面檢查有沒有這個(gè)ID存在,有的話則表示這個(gè)轉(zhuǎn)賬已經(jīng)處理過了,直接把這個(gè)event忽略掉;沒有的話則表示需要處理這個(gè)event,執(zhí)行轉(zhuǎn)賬??傮w來講,這樣的處理邏輯就是冪等的。
當(dāng)然,實(shí)際的轉(zhuǎn)賬系統(tǒng)還需要考慮各種錯(cuò)誤情況,比如:轉(zhuǎn)入方處理失敗的話,可以發(fā)送一個(gè)反向的event,轉(zhuǎn)出方把之前的扣錢revert回來。
案例二:數(shù)據(jù)遷移
在之前的文章,也有多次提到數(shù)據(jù)遷移這個(gè)案例。這個(gè)案例說的是需要把數(shù)據(jù)從老的數(shù)據(jù)庫(kù)遷移到新的數(shù)據(jù)庫(kù),并且需要保證服務(wù)不停止(zero downtime),即不影響用戶的正常使用。
對(duì)于老數(shù)據(jù),可以直接使用一個(gè)background job不斷的遷移;關(guān)鍵是對(duì)于新數(shù)據(jù),應(yīng)該如何“遷移”?一種辦法是:雙寫,即在往老數(shù)據(jù)庫(kù)寫的同時(shí)也往新數(shù)據(jù)庫(kù)寫,這樣來保證新數(shù)據(jù)在兩邊都有。
同時(shí)往兩個(gè)數(shù)據(jù)庫(kù)寫,如何保證兩邊全成功全失敗呢?這又是分布式事務(wù)的問題,當(dāng)時(shí)提到了一種方案:best effort 1pc,使用的是Spring提供的ChainedTransactionManager。但是,這種方式在極限情況下也會(huì)出現(xiàn)不一致的情況,比如:數(shù)據(jù)庫(kù)在特定的時(shí)間節(jié)點(diǎn)宕機(jī)。
下面介紹另外一種基于event方式的雙寫:在把數(shù)據(jù)往老數(shù)據(jù)庫(kù)寫之后,接著把數(shù)據(jù)本身作為event payload發(fā)到Kafka。(這里可以利用outbox pattern來保證at least once delivery)然后,新加一段邏輯,監(jiān)聽這個(gè)event,收到這個(gè)event之后,把數(shù)據(jù)寫入到新的數(shù)據(jù)庫(kù)。
同樣的,在監(jiān)聽event這里,需要額外handle下面的情況以保證冪等性:
收到重復(fù)插入數(shù)據(jù)event(這個(gè)情況和上面轉(zhuǎn)賬的案例類似)
對(duì)于這種情況,如何實(shí)現(xiàn)冪等性處理?
類似的,可以依賴一個(gè)唯一的主鍵,先根據(jù)主鍵判斷數(shù)據(jù)存不存在。
消息順序變化
消息順序產(chǎn)生變化,可能的情況有:
對(duì)于這種情況,如何保證冪等性呢?
關(guān)鍵點(diǎn)是老的event需要被忽略掉。實(shí)現(xiàn)層面可以依賴于一個(gè)時(shí)間戳,不管是遷移數(shù)據(jù)本身,或者是event對(duì)象本身,如果新的event已經(jīng)處理,則老的event忽略;如果數(shù)據(jù)已經(jīng)被更新,則老的event忽略。
上面提到的雙寫需要再額外增加一個(gè)event數(shù)據(jù)庫(kù)表,如果可以,也可以采用cdc的方式,這種方式常常用于數(shù)據(jù)庫(kù)的復(fù)制、備份等場(chǎng)景,利用這種方式,則不需要額外寫一張表,而依賴數(shù)據(jù)庫(kù)的事務(wù)日志,具體可以參考我的另一篇文章(空談發(fā)件箱模式(outbox pattern))。
感謝各位的閱讀,以上就是“分布式系統(tǒng)如何實(shí)現(xiàn)冪等性”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)分布式系統(tǒng)如何實(shí)現(xiàn)冪等性這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!