NO IMAGE

原文連結:http://www.jianshu.com/p/716d3ec567c0

問題的起源

分散式系統的特性

對分散式系統有過研究的讀者,可能聽說過“CAP定律”、“Base理論”等,非常巧的是,化學理論中ACID是酸、Base恰好是鹼。這裡我們不對這些概念做過多的解釋,有興趣的讀者可以檢視相關參考資料。

這裡針對一致性我們做個簡單的科普:

分散式事務有強一致,弱一致,和最終一致性這三種:

強一致

當更新操作完成之後,任何多個後續程序或者執行緒的訪問都會返回最新的更新過的值。這種是對使用者最友好的,就是使用者上一次寫什麼,下一次就保證能讀到什麼。根據 CAP 理論,這種實現需要犧牲可用性。

弱一致

系統並不保證續程序或者執行緒的訪問都會返回最新的更新過的值。系統在資料寫入成功之後,不承諾立即可以讀到最新寫入的值,也不會具體的承諾多久之後可以讀到。

最終一致

弱一致性的特定形式。系統保證在沒有後續更新的前提下,系統最終返回上一次更新操作的值。在沒有故障發生的前提下,不一致視窗的時間主要受通訊延遲,系統負載和複製副本的個數影響。DNS 是一個典型的最終一致性系統。
在分散式系統中,同時滿足“CAP定律”中的“一致性”、“可用性”和“分割槽容錯性”三者是幾乎不可能的。在網際網路領域的絕大多數的場景,都需要犧牲強一致性來換取系統的高可用性,系統往往只需要保證“最終一致性”,只要這個最終時間是在使用者可以接受的範圍內即可,這時候我們只需要用短暫的資料不一致就可以達到我們想要效果。

例項描述

比如有訂單,庫存兩個資料,一個下單過程簡化為,加一個訂單,減一個庫存。 而訂單和庫存是獨立的服務,那怎麼保證資料一致性。

這時候我們需要思考一下,怎麼保證兩個遠端呼叫“同時成功”,資料一致?

請大家先注意一點遠端呼叫最鬱悶的地方就是,結果有3種,成功、失敗和超時。 超時的話,成功失敗都有可能。

一般的解決方案,大多數的做法是藉助mq來做最終一致。

如何實現最終一致

例項分析

我們是怎麼利用Mq來達到最終一致的呢?下面讓我們來一起進行詳細的分析:

訂單業務分析

首先,拿我們上面提到的訂單業務舉例:

在我們進行加訂單的過程中同時插入logA(這個過程是可以做本地事務的)
然後可以非同步讀取logA,發mqA
B端接收mqA,同時減少庫存,B這裡需要做冪等(避免因為重複訊息造成的業務錯亂)

複雜的混合非同步業務呼叫

那麼我們通過上面的分析可能聯想到這樣的問題?

本地先執行事務,執行成功了就發個訊息過去,消費端拿到訊息執行自己的事務。
比如a,b,c a非同步呼叫b,c, 如果b失敗了,或者b成功,或者b超時,那麼怎麼用mq讓他們最終一致呢?b失敗就失敗了,b成功之後給c發一個訊息,b和c對a來講都是非同步的,且他們都是同時進行的話,而且需要a,b,c同時成功的情況,那麼這種情況用mq怎麼做?
其實做法還是參照於本地事務的概念的。

第一種情況:假設a,b,c三者都正常執行,那整個業務正常結束

第二種情況:假設b超時,那麼需要a給b重發訊息(記得b服務要做冪等),如果出現重發失敗的話,需要看情況,是終端服務,還是繼續重發,甚至人為干預(所有的規則制定都需要根據業務規則來定)

第三種情況:假設a,b,c三者之中的一個失敗了,失敗的服務利用MQ給其他的服務傳送訊息,其他的服務接收訊息,查詢本地事務記錄日誌,如果本地也失敗,刪除收到的訊息(表示訊息消費成功),如果本地成功的話,則需要呼叫補償介面進行補償(需要每個服務都提供業務補償介面)。

注意事項

mq這裡有個坑,通常只適用於只允許第一個操作失敗的場景,也就是第一個成功之後必須保證後面的操作在業務上沒障礙,不然後面失敗了前面不好回滾,只允許系統異常的失敗,不允許業務上的失敗,通常業務上失敗一次後面基本上也不太可能成功了,要是因為網路或宕機引起的失敗可以通過重試解決,如果業務異常,那就只能發訊息給a和c讓他們做補償了吧?通常是通過第三方進行補償,ABC提供補償介面,設計正規化裡通常不允許消費下游業務失敗

上面的話我們該怎麼理解呢,舉個例子吧:

比如A給B轉賬,A先自己扣錢,然後發了個訊息,B這邊如果在這之前銷戶了,那重試多少次也沒用,只能人工干預。

阿里在分散式事務採用的解決方式

阿里部分業務是用Mq實現了最終一致性,也有一部分業務用了tcc事務,但是tcc事務用的比較少,因為會侵染業務,開發成本比較高,如果體量不大的話直接用jta或mq支援事務就好,其實在分散式事務這一塊還有一種最大努力型,也比較無腦的一種方式。

文/jsondream(簡書作者)