前提
- 所有服務均有獨立的事物管理機制,相互間沒有任何關聯.
- 所有業務介面都有對應的補償方法,用于將已經更新的資料還原到上一次的狀態.
- 本次實體為同步業務,理想狀態下,只有全部成功或全部失敗兩種情況.
正式開始
正常流程

一切安好.
中途例外 - 補償成功

雖然發生了失敗,但所有補償都成功了.沒有什么問題
中途例外 - 補償失敗

此時,主服務有三種處理方法
- 主服務無限重試補償方法,直到補償成功.
這里有很麻煩的問題,如果下游的服務器已經停機,此時主服務的無限重試已經沒有意義.在最壞的情況下,如果主服務訪問量過大,而因為業務相同,主服務的執行緒池中的全部執行緒將全部處于阻塞狀態,失去處理新請求的能力.
同時,訪問主服務的客戶端有可能會放棄本次的連接,導致正在重試中的執行緒被回收,丟失所有狀態 - 主服務盡可能呼叫補償方法,并回滾自身事物,同時通知備用方案
這里依靠備用方案對失敗的補償呼叫進行異步異步呼叫,已達到"最終一致"的效果.備用方案一般需要集成"可靠訊息系統" - 主服務直接放棄補償與自身的回滾,并通知備用方案
與2基本相同.在呼叫鏈比較長的情況下且補償隨機性失敗,從上層看,2方案的呼叫結果(備用方案未執行時)將成鋸齒狀.而本方案,其對應
的消費者直接以級聯方式進行補償的呼叫,最終完成全部補償呼叫與主方法資料回滾,同樣從上層看,方案3的呼叫結果(備用方案未執行時)將是平整的(只有失敗處斷裂).
方案1直接pass
方案2,3可以使用訊息佇列與對應的消費者進行實作.但是會有短暫的資料不一致問題
中途例外(偽) - 觸發補償

這種情況具體描述如下
- 正常遠程呼叫
- 下游業務接收并進行處理
- 主服務認為網路超時(如等待資料庫鎖釋放)或發生其他意外,從而觸發補償流程
- 下游業務完成請求處理
- 主服務發起并執行補償流程(不包含4涉及的服務)
解決方法
- 記錄全域唯一識別碼,當主服務發起補償時,所有下游業務應該得知,并出發各自的補償方法.
- 需要注意,在嘗試解決程序中,如果主服務過早推送回滾通知,涉及獨自提交的服務早于對應業務處理完成進行補償,將會導致回滾通知失效.
在這種情況下,上游業務回滾,下游業務獨自完成了業務處理,造成資料問題. 會有短暫的資料不一致問題
同樣的,在這種情況下,呼叫補償也有可能發生呼叫失敗的情況.并且會更復雜,因為此時主服務會發出全域的回滾訊息,需要處理補償訊息與回滾訊息的順序問題
主服務斷電 - 正常運行,補償中,補償失敗中


此時主服務狀態全部丟失且下游業務狀態錯亂
只能借用可靠訊息對進行中的操作進行記錄,并在再次開啟后進行恢復
總結
- 在必須要使用RPC進行遠程呼叫且事物復雜的情況下,應使用一個可靠的訊息系統保證在各方斷電,斷網,回滾時能夠即使恢復"狀態".
- 同時,應保證嚴格的冪等性,對于非冪等訊息,根據實際情況,進行保留會消耗.
- 所有經由訊息系統的恢復性呼叫,均為異步操作,此時各方資料會出現不同步的問題.
- 對于復雜性,即使是只有2步,也會產生意外,且補償方法并不可靠.必須使用"可靠訊息"系統進行保證(就是說,不要想要有"方便"這兩個字).
- 對于呼叫訊息,進行兩步驗證,要做xxx與完成xxx,同時保證復蘇用的業務資料即可.對于全域回滾訊息,進行通用,同時保證復蘇訊息與回滾訊息的冪等
- 主服務自身的業務邏輯也應做成遠程呼叫的模式,即有常規與補償方法.而主方法本身不包含事物,以此進行統一管理
- 基于上述的內容,已經很靠近TCC模式了,且需要擴展很多框架級代碼與補償代碼.
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/44281.html
標籤:架構設計
