Compensating-Transaction模式

在應用中,會將一系列相關的操作定義爲一個連續的操作,當其中一個或者多個步驟失敗的時候,Compensating-Transaction模式會重置(回滾)這個連續的操作。在雲應用中,這些需要保證一致性的操作是較爲常見的,正是這些操作構成了複雜的業務處理和工作流。

問題

雲中運行的應用會頻繁的修改數據。數據可能會傳播到另外一組數據源,但是數據庫卻可能在不同的地方。爲了避免競爭並且增加諸如此類分佈式環境的性能,應用應該儘量少的提供強事務一致性的功能。應用可以實現最終一致性的。在最終一致性模型中,大多數業務操作都會包含多個自主的子步驟。這些子步驟在執行的過程中,整個系統的狀態可能是不一致的,但是當所有的子步驟都執行完畢的時候,整個系統狀態會重新變成一致的。

Data Consistency Primer章節中對分佈式事務的規模化,以及設計最終一致模型的一些準則提供了更多的信息。

在最終一致模型中,一個嚴峻的問題就是一旦其中一個步驟發生了嚴重的錯誤,該如何處理。一旦發生了嚴重的錯誤的話,回滾之前的工作就十分必要了。然而,數據是無法簡單的回滾的,因爲其他應用的併發實例可能在那之後仍然改變數據再次覆蓋。甚至就算是併發實例的狀態並沒有改變的情況下,回滾也不是簡單的恢復到原始狀態就能解決的。可能還需要應用一些應用所特有的邏輯。

如果實現了最終一致性的操作會跨越多個數據倉庫的話,回滾這個操作就需要按相反的順序重新訪問所有數據倉庫了。而每個數據倉庫的回滾操作也必須完成,才能防止系統出現數據的不一致。

並不是所有的實現數據一致性的操作訪問的數據都在同一個數據庫中的。在SOA環境中,一個操作很可能會調用另一個Service服務,從而引起另一個服務狀態的改變。爲了回滾之前的操作,調用的Service的狀態也必須回滾。這可能就會需要再次調用Service,並且執行相反的操作纔可以。

解決方案

基於前面的問題,可以考慮實現Compensating-Transaction模式。在Compensating-Transaction模式中,所有的步驟都必須能夠重置之前的工作。Compensating-Transaction可能無法僅僅通過將之前的狀態替換掉現在的狀態這種方式來回滾,前面已經提到過了,因爲這個方法可能會覆蓋或者被其他併發實例的操作所覆蓋。所以,回滾必須相當的智能,考慮到所有併發應用實例所造成的影響。同時這種回滾行爲通常是與應用相關的,要取決於應用執行操作的一些具體的特性。

通常實現最終一致性的操作就是通過workflow來補償之前的操作。隨着操作的進行,系統記錄每一步所執行的信息,以及操作執行後該如何回滾。如果操作在任何一個步驟失敗了,workflow會倒回所有已經完成的操作,並根據記錄的信息進行回滾。需要注意的是,Compensating-Transaction沒有必要按照之前執行順序的逆序來依次執行回滾操作,Compensating-Transaction可能將某些操作以並行的方式來完成的。

這個方法類似於Sagas策略,具體可以參考Sagas模式

Compensating-Transaction本身也是一種最終一致性的操作,所以也可能會失敗的。系統應該能夠同樣在Compensating-Transaction失敗的步驟進行重試和繼續執行。有時候,重複之前失敗的操作也是很有必要的。所以Compensating-Transaction中的操作都應該定義爲冪等的操作。關於冪等的更多的信息,可以參考冪等模式
在一些情況下,可能是無法以非手動的方式來執行回滾的。在這些情況下,系統就需要發出報警,並且提供足夠的信息給操作人員,讓操作人員可以瞭解產生錯誤的原因,以及如何恢復回來。

需要考慮的問題

在實現Compensating-Transaction模式的時候,需要考慮如下的問題:

  • 在考慮實現最終一致性的時候,有時判斷某個步驟是否失敗可能很難。因爲其中的一步可能不是立刻返回失敗信息,而是持續的阻塞,所以考慮使用一些超時的機制是十分必要的。
  • 補償邏輯是很難抽象和泛華的。所以Compensating-Transaction一般都是基於應用特性的,這也令Compensating-Transaction十分依賴應用的業務,只有足夠的信息才能回滾每個失敗的步驟。
  • 開發者實現Compensating-Transaction的時候最好將其中的每一個步驟都定義爲冪等的。這樣可以在Compensating-Transaction回滾中某些步驟失敗的時候可以從容的進行重試,而不用擔心重試操作會影響到服務的狀態。
  • 整個架構要處理正常流程中操作和Compensating-Transaction必須是可恢復的。補償邏輯所需要的信息不可或缺。而且架構必須能夠可靠地監控補償邏輯的處理過程。防止出現循環等狀況。
  • Compensating-Transaction只補償失敗步驟之前執行成功了的操作。
  • 在Compensating-Transaction的回滾過程中,回滾操作的執行順序不必非要以之前執行的順序的逆序來依次回滾。比如,如果其中一個數據倉庫對於數據一致性更加敏感,那麼優先補償該數據倉庫就更爲重要。
  • 最好在每個需要完成操作的資源上面加上一個超時鎖,並且提前獲取相應的資源,這樣可以增加整個事物的成功率。而所有的工作最好在獲取了全部的資源之後再執行。所有的操作必須在鎖過期之前全部完成。
  • Retry模式同時應用可以更好的減少錯誤,從而減少回滾操作。如果整個操作中的某個步驟失敗了,儘量將這個失敗當成一個臨時錯誤來進行重試。只有當操作不斷的失敗或者無法修復的時候再去執行Compensating-Transaction的回滾操作。
    關於實現Compensating-Transaction中的很多問題,其實跟實現最終一致性所需要考慮的是一樣的。可以查看Data Consistency Primer來了解更多的內容。

何時使用該模式

當某個操作中一旦出現失誤必須回滾的時候可以考慮這個模式。如果可能的話,最好儘可能減少Compensating-Transaction的複雜性,更多信息可以參考Data Consistency Primer。

Compensating-Transaction使用舉例

某個旅遊網站支持用戶來預訂行程(包括機票以及酒店等)。一個行程可能包含多個航班以及酒店信息。假設一名旅客需要從西雅圖到倫敦,然後到巴黎,在飛回西雅圖的話,可能就會產生如下的行程:

  1. 預訂某航班F1從西雅圖飛往倫敦。
  2. 預訂某航班F2從倫敦飛往巴黎。
  3. 預訂某航班F3從巴黎飛往西雅圖。
  4. 預訂倫敦的某個酒店H1
  5. 預訂巴黎的某個酒店H2

儘管上述的每個操作都是一個獨立的原子行爲,但是他們也構成了一個最終一致性操作。因此,隨着上述操作的執行,系統也必須記錄所有執行過的操作的補償操作,一旦旅客想要取消行程,就要撤銷整個之前的操作。所以這些補償的操作就可以作爲一個Compensating-Transaction來執行。

這裏寫圖片描述

需要注意的是,Compensating-Transaction中的步驟其實並不需要完全按照之前執行的相反順序來依次回滾的。而且Compensating-Transaction中的每個步驟都必須考慮到其相關的業務規則。例如,退訂某個航班可能並不會返還給顧客當初所支付的全部金額。

在Compensating-Transaction中所執行的回滾操作也可能並行來執行的,完全取決於每一步的補償邏輯具體是如何設計的。

在有些商業解決方案中,系統中某個步驟的失敗也並不會破事整個系統,所以不需要通過Compensating-Transaction來回滾全部操作。仍然拿上面的例子來說,如果在預訂了航班F1,F2,F3之後,客戶無法預訂到酒店H1的房間了,那麼更好的選擇是給客戶提供一個其他酒店的房間而不是取消整個行程了。當然,旅客可能仍然會選擇取消,但是這個選擇的權利應該交給顧客而不是系統來幫助顧客取消了。

相關的其它模式

在實現Compensating-Transaction的時候,下面的模式以及相關信息也可以進行參考:
  • Data Consistency Primer Compensating-Transaction通常被用來回滾需要實現最終一致性模型的功能。Data Consistency Primer中的內容提供了更多關於最終一致性的特性的說明。
  • Scheduler-Agent-Supervisor 模式。該模式描述瞭如何實現一個儘可能利用分佈式服務以及資源的彈性系統。在很多場景下,也會需要使用Compensating-Transaction來回滾失敗的錯誤。
  • Retry模式。Compensating-Transaction實現起來代價是非常昂貴的,所以其中的一些步驟最好包含重試邏輯以便增加最終一致性模型的成功率,減少回滾的概率。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章