大規(guī)模事務(wù)處理系統(tǒng)如大型電子商務(wù)網(wǎng)站后臺中,分布式事務(wù)是個繞不過去的挑戰(zhàn),而微服務(wù)架構(gòu)的流行更加讓這個問題日益突出。這表現(xiàn)在幾個方面:首先,數(shù)據(jù)需要保證嚴(yán)格一致,任何訂單,支付,庫存等等地管理都不允許有任何錯誤;其次,業(yè)務(wù)邏輯常常需要多個服務(wù)的更新,因此解決“部分失效”問題是這類系統(tǒng)不得不面對的問題。 例如圖中服務(wù)A和服務(wù)B,在一次事務(wù)處理中均需要調(diào)用,那么如果服務(wù)B發(fā)生崩潰,或者網(wǎng)絡(luò)錯誤,服務(wù)A應(yīng)當(dāng)如何處理?如果服務(wù)A發(fā)生崩潰,那么恢復(fù)后需要詢問服務(wù)B是否重做剛才沒有完成的事務(wù)么? 如何確保服務(wù)之間的數(shù)據(jù)一致是這類架構(gòu)挑戰(zhàn)需要解決的核心問題。根據(jù)Weikum和Vossen合著的Transactional Information Systems書中描述,有3種手段用來處理這種情形: 第一種是引入分布式事務(wù),最常見的就是2PC兩階段提交。如下圖所示: 應(yīng)用2PC在SOA架構(gòu)體系里,并不是推薦的做法,因為這其實是違反了進行服務(wù)治理的初衷,此外,2PC本身的缺點也不可忽視,例如DTC的可用性問題會導(dǎo)致死鎖,2PC帶來的鎖引發(fā)的性能開銷等。服務(wù)級別2PC的協(xié)議并不統(tǒng)一,例如Jboss社區(qū)的Narayana就是一個實現(xiàn)了一系列分布式事務(wù)協(xié)議的事務(wù)處理器,目前包含XATMI,JTA,JTS,Web-Service Transactions,以及Jboss自有的REST Transactions等等一系列事務(wù)協(xié)議。 第二種稱為“無狀態(tài)隊列事務(wù)”。采用消息隊列和最終一致性來進行服務(wù)間解耦。為了確保數(shù)據(jù)的一致,一個服務(wù)必須能夠在其領(lǐng)域所在的數(shù)據(jù)庫發(fā)生變化時,原子地把該變化發(fā)布成事件。 這種技術(shù)手段具備很高的性能和伸縮性,可以避免分布式事務(wù)帶來的性能和可用性瓶頸。但缺點在于如果事務(wù)涉及到多條記錄操作,處理就比較困難。另外,如圖中所示意,經(jīng)常的工程手段會導(dǎo)致服務(wù)內(nèi)“雙寫”,意思是數(shù)據(jù)庫和消息隊列需要同時寫入。在任何涉及到多個存儲的事務(wù)操作里都不可避免地涉及到分布式事務(wù),因此很可能在服務(wù)內(nèi)引入XA這樣的2PC。此外還需要面臨的挑戰(zhàn)包括如何做到“冪等”操作,以及消息隊列的順序問題。如何避免雙寫和保證消息的順序,我們可以從本公眾號之前介紹的CDC(Change Data Capture)模式以及日志為中心的架構(gòu)中得到啟示。 印度最大電商Flipkart就是采用了這種手段解決跨服務(wù)的數(shù)據(jù)一致問題,并成功應(yīng)用在供應(yīng)鏈,訂單,支付環(huán)節(jié)。除了消息隊列之外,F(xiàn)lipkart引入2個設(shè)計,一個是冪等過濾器,做到每個事務(wù)操作在開始之前確保唯一性。另一個是重試隊列,也就是說消息隊列有2個部分組成,還有一個重試隊列用于存放因服務(wù)失效而失敗的事務(wù)。換句話,F(xiàn)lipkart不提供事務(wù)回滾。 另一種解決服務(wù)內(nèi)雙寫問題的做法是借助于'Event Sourcing'和CQRS的設(shè)計。Event Sourcing的核心理念是只持久化狀態(tài)改變的數(shù)據(jù)。例如下圖的例子中,最終持久化的是下部的狀態(tài)變化記錄列表,持久化的數(shù)據(jù)被稱為Event Store,可以根據(jù)Entity的主鍵檢索。 光有Event Sourcing還不足以解決問題,還需要借助于CQRS(Command Query Responsibility Separation)。因為Event Sourcing只提供了業(yè)務(wù)邏輯的狀態(tài)變化,但缺乏最終的查詢視圖,而CQRS的目的就是分離查詢視圖和業(yè)務(wù)邏輯。 組合Event Sourcing和CQRS來提供跨服務(wù)之間分布式事務(wù)的一個例子如下圖所示,相關(guān)代碼可以從https://github.com/cer/event-sourcing-examples來得到。 無狀態(tài)隊列事務(wù)這種手段在Pat Helland關(guān)于分布式系統(tǒng)的著名必讀論文'Life beyond Distributed Transactions: an Apostate's Opinion'也有類似表達(dá)。Pat Helland認(rèn)為應(yīng)當(dāng)盡可能避免分布式事務(wù),為此系統(tǒng)中引入三種抽象角色:Entity,Message,Activity。Entity表示在單個服務(wù)內(nèi)事務(wù)操作的數(shù)據(jù),Message用于在分布式事務(wù)涉及到的多個服務(wù)之間進行消息通知,而Activity則用于Entity記錄狀態(tài),便于在出錯時進行回滾。Google的Percolator就利用這種語義實現(xiàn)了分布式事務(wù)。通常,Activity可用Paxos來實現(xiàn),在已有的系統(tǒng)里,完全實現(xiàn)Pat Helland的Activity抽象并不多見,像上述的Flipkart通過冪等過濾來消除Activity抽象的做法是普遍手段。 第三種做法是事務(wù)補償。事務(wù)補償即在事務(wù)鏈中的任何一個正向事務(wù)操作,都必須存在一個完全符合回滾規(guī)則的可逆事務(wù)。如果是一個完整的事務(wù)鏈,則必須事務(wù)鏈中的每一個業(yè)務(wù)服務(wù)或操作都有對應(yīng)的可逆服務(wù)。有兩種形式的事務(wù)補償,一種是無狀態(tài)補償,既補償本身作為另外的事務(wù),通過工作流引擎提供額外業(yè)務(wù)邏輯。無狀態(tài)事務(wù)補償實際上是一種基于最終一致的反操作,因此性能相比XA要好很多,但它通常用于長期運行的服務(wù),并且在出錯時可能還需要人工干預(yù)。另一種是有狀態(tài)補償,就是說補償操作作為事務(wù)處理的第二階段來進行。TCC(Try-Commit-Cancel)就是這種方式的補償。TCC是一種融合了事務(wù)補償機制跟XA的做法。與XA協(xié)議比較,TCC位于業(yè)務(wù)服務(wù)層而非數(shù)據(jù)資源層,沒有單獨的準(zhǔn)備階段,因此TCC讓服務(wù)之間的耦合更松。跟事務(wù)補償相比,TCC消除了正向補償?shù)男枨?,但是性能和可擴展性都比較差。 大型SOA架構(gòu)體系的事務(wù)處理,可以從網(wǎng)上找到支付寶程立在8年前的幻燈片,這也是這個話題比較全面的概括 |
|