MySQL
的事務是數據一致性的典範,事務內的執行要麼都成功,要麼都失敗。但業務系統涉及系統間的相互調用,涉及的數據庫也不盡相同,因此實現數據一致性仍是有挑戰的。數據庫
首先了解強一致性和弱一致性。在微服務中,系統間經過HTTP
的方式相互調用,很難實現數據的強一致。咱們這裏主要說弱一致性,也就是數據最終一致性。異步
數據一致性還有個重要的前提:支持冪等。也就是說,只要請求參數不變,那麼不管重複請求多少次,結果都同樣。在對接第三方支付時,這個詞出現的頻率仍是老高的。微服務
蝸牛要在一家電商網站買電子書,整個購買流程和涉及的系統虛構以下圖。過程涉及檢查它是否已經買過,而後是生成訂單號、支付、交付(實際上訂單系統不包含支付功能,這裏簡化處理)。性能
<center>網站
</center>spa
交付涉及三個系統,在任何一個系統內,數據庫的事務都只能保證它服務內的數據一致。並且,若是在事務過程當中引入了調用第三方的HTTP
請求,數據庫的事務執行結果甚至有可能會被污染。好比,HTTP
請求超時返回失敗,但實際上請求卻執行成功的場景。設計
參考以前寫的 Saga Pattern模式,對任何一個外部服務的調用都引入兩個行爲:執行和補償。補償是對執行結果的修正。好比對於用戶支付失敗的場景,補償行爲能夠是接口重試、能夠是直接退款、還能夠推送MQ
異步修復等。code
統一使用interface
來定義一套規範。每一種支付方式以及購買產品所調用的外部服務可能不盡相同,用interface
來達到統一調用的目的。補償的行爲都基於執行動做返回的錯誤,因此咱們須要實現本身的錯誤碼。blog
type DeliverPattern interface { //是否須要執行交付流程 Check(ctx *context.Context) (bool, error) //支付及支付補償 DoPay(ctx *context.Context) error PayCompensate(ctx *context.Context, doErr error) error //交付及對應的補償 DoDeliver(ctx *context.Context) DeliverCompensate(ctx *context.Context, doErr error) error }
對於如何補償,不一樣的業務有不一樣的補償方式,當讓不能一律而論。但總體的思想,我以爲仍是不外乎兩種。固然,下面的兩種描述是本身這樣稱呼的。接口
事務類
首先即是數據庫事務
類,任何一個流程失敗,整個事務內的操做所有反向回滾。沿着這樣的思路,接口定義中PayCompensate
應該實現DoPay
的回滾操做,而DeliverCompensate
應該實現DoPay
以及DoDeliver
的回滾操做。
咱們須要在操做的同時維護一個回滾操做的隊列,任何一個Do
行爲的完成,都須要在回滾隊列中插入對應的回滾方法。當後面任何一個Do
操做失敗,統一執行回滾隊列的方法。
這樣的困境在於你不能徹底保證回滾方法必定成功執行。並且出於性能考慮,還須要結合異步隊列,經過後臺重試來保證整個業務流程完全回滾成功或回滾失敗。
狀態類
每一個業務都會拆分紅各個更小的塊,就跟寫代碼空行同樣,這裏的DeliverPattern
也是根據業務流程拆分紅更小的執行粒度。咱們能夠爲每一個Do
行爲都設置一個狀態碼,相似於狀態機,記錄每一次購買的各個狀態。
const ( StatusDoPaySuccess = 1 StatusDoPayCompensateSuccess = 2 StatusDoPayCompensateFailure = 3 )
這樣咱們補償方法中執行的再也不是回滾操做,而是Do
方法的重試。若是補償成功,繼續執行後續的操做,若是補償失敗,記錄下該狀態,後續看看怎麼補償。