SpringCloud(四) 微服務架構-事務一致性
分散式事務指事務的操作位於不同的節點上,需要保證事務的 AICD 特性。目前比較常用的分散式事務解決方案包括強一致性的兩階段提交協議、三階段提交協議以及最終一致性的可靠事件模式、補償模式、阿里的TCC模式。
事務是指由一組操作組成的一個工作單元,這個工作單元具有原子性(atomicity)、一致性(consistency)、隔離性(isolation)和永續性(durability)。 原子性:執行單元中的操作要麼全部執行成功,要麼全部失敗。如果有一部分成功一部分失敗那麼成功的操作要全部回滾到執行前的狀態。 一致性:執行一次事務會使用資料從一個正確的狀態轉換到另一個正確的狀態,執行前後資料都是完整的。
隔離性:在該事務執行的過程中,任何資料的改變只存在於該事務之中,對外界沒有影響,事務與事務之間是完全的隔離的。只有事務提交後其它事務才可以查詢到最新的資料。 永續性:事務完成後對資料的改變會永久性的儲存起來,即使發生斷電宕機資料依然在。
強一致性
兩階段提交協議
在分散式系統中,為了解決多個節點之間的協調問題,就需要引入一個協調者負責控制所有節點的操作結果,要麼全部成功,要麼全部失敗。其中,XA協議是一個分散式事務協議,它有兩個角色:事務管理者和資源管理者。我們可用把事務管理者理解為協調者,資源管理者理解為參與者。XA協議通過二階段提交協議保證強一致性:
第一階段準備:事務管理者向資源管理者發起準備指令,詢問資源管理者預提交是否成功;資源管理者執行操作,並不提交,最後給出自己的響應結果
第二階段提交:如果全部資源管理者都回復預提交成功,事務管理者則發起正式提交命令;如果其中一個資源管理者回復預提交失敗,事務管理者則發起全部回滾命令
二階段提交協議的缺點
- 同步阻塞問題:執行過程中,所有參與節點都是事務阻塞型的。當參與者佔有公共資源時,其他第三方節點訪問公共資源不得不處於阻塞狀態。
- 單點故障:由於協調者的重要性,一旦協調者發生故障。參與者會一直阻塞下去。尤其在第二階段,協調者發生故障,那麼所有的參與者還都處於鎖定事務資源的狀態中,而無法繼續完成事務操作。(如果是協調者掛掉,可以重新選舉一個協調者,但是無法解決因為協調者宕機導致的參與者處於阻塞狀態的問題)
- 資料不一致:在二階段提交的階段二中,當協調者向參與者傳送commit請求之後,發生了局部網路異常或者在傳送commit請求過程中協調者發生了故障,這回導致只有一部分參與者接受到了commit請求。而在這部分參與者接到commit請求之後就會執行commit操作。但是其他部分未接到commit請求的機器則無法執行事務提交。於是整個分散式系統便出現了資料部一致性的現象。
三階段提交協議
三階段提交協議與二階段的不同之處在於引入了超時機制來解決同步阻塞問題,此外加入預備階段,儘可能最早發現無法執行的資源管理者並終止事務。
最終一致性
TCC模式
TCC模式將一個任務拆分為3個操作:Try、Confirm和Cancel。在TCC模式中,主業務服務負責發起流程,而從業務服務提供TCC模式的Try、Confirm和Cancel三個操作,還有一個事務管理器的角色負責控制事務的一致性。事實上,TCC模式也是一種二階段提交。
使用者接入TCC,最重要的是考慮如何將自己的業務模型拆成兩階段來實現。
例如:賬戶業務服務,將業務劃分為資源(餘額)檢查與預留階段 與 執行扣款或回滾階段。
-
Try 方法作為一階段準備方法(做資源的檢查和預留)
- 在扣錢場景下,Try 要做的事情是就是檢查賬戶餘額是否充足,預留轉賬資金,預留的方式就是凍結 A 賬戶的 轉賬資金。
- Try 方法執行之後,賬號 A 餘額雖然還是 100,但是**其中 10 元已經被凍結了,不能被其他事務使用**。
二階段 Confirm 方法(執行真正的扣錢操作)
-
Confirm 會使用 Try 階段凍結的資金,執行賬號扣款。Confirm 方法執行之後,賬號 A 在一階段中凍結的 10 元已經被扣除,賬號 A 餘額變成 70 元 。
-
如果二階段是回滾的話,就需要在 Cancel 方法內釋放一階段 Try 凍結的 10 元,使賬號 A 的回到初始狀態,100 元全部可用
需要注意的是,第二階段 confirm 或 cancel 操作本身也是滿足最終一致性的過程,在呼叫 confirm 或 cancel 的時候也可能因為某種原因(比如網路)導致呼叫失敗,所以需要活動管理支援重試的能力,同時這也就要求 confirm 和 cancel 操作具有冪等性(所謂冪等,就是任意多次執行所產生的影響均與一次執行的影響相同)。如果業務服務向 TCC 服務框架提交confirm/cancel 失敗,不會導致不一致,因為服務最後都會超時而取消。TCC的實現框架有很多成熟的開源專案,比如tcc-transaction。它通過@Compensable切面進行攔截,可以透明化對參與者confirm/cancel方法的呼叫,從而實現TCC模式
補償模式
除了重試機制,還可以在每次更新時進行修復。定時校對也是一種重要的解決手段。業內比較常用的有單機場景下的Quartz以及分散式場景下的XXL-JOB等。
可靠事件模式
可靠事件模式是指通過引入可靠的訊息佇列,只要保證當前的可靠事件投遞並且訊息佇列確保事件傳遞至少一次,那麼訂閱這個事件的消費者保證事件能夠在自己的業務內被消費即可。但是在網路通訊過程中,上下游可能因為各種原因而導致訊息丟失。因此,需要通過“正反向訊息機制”確保訊息佇列實現可靠的事件傳遞,並且使用補償機制儘可能在一定時間內將未完成的訊息重新投遞。
一般做法,是主業務服務將要傳送的訊息持久化到本地資料庫中,設定標誌狀態為“待發送”;然後把訊息傳送給訊息佇列,訊息佇列收到訊息後,也把訊息持久化到其儲存服務中,但並不是立即向從業務服務(生產者)投遞訊息,而是先向主業務服務(生產者)返回訊息佇列的響應結果,然後主業務服務判斷響應結果執行之後的業務處理。如果響應失敗,則放棄之後的業務處理,設定本地的持久化訊息標誌狀態為“結束”狀態。否則,執行後續的業務處理,設定本地的持久化訊息標誌狀態為“已傳送"狀態。
Apache RockerMQ
Apache RockerMQ是阿里開源的分散式訊息中介軟體,4.3版本正式支援分散式事務訊息。RockerMQ事務訊息中介軟體解決了生產者端的訊息傳送與本地事務執行的原子性問題。 換句話說,本地事務執行不成功,則不會進行MQ訊息推送。
MQ訊息、DB操作一致性方案:
1)傳送訊息到MQ伺服器,此時訊息狀態為SEND_OK。此訊息為consumer不可見。
2)執行DB操作;DB執行成功Commit DB操作,DB執行失敗Rollback DB操作。
3)如果DB執行成功,回覆MQ伺服器,將狀態為COMMIT_MESSAGE;如果DB執行失敗,回覆MQ伺服器,將狀態改為ROLLBACK_MESSAGE。注意此過程有可能失敗。
4)MQ內部提供一個名為“事務狀態服務”的服務,此服務會檢查事務訊息的狀態,如果發現訊息未COMMIT,則通過Producer啟動時註冊的TransactionCheckListener來回調業務系統,業務系統在checkLocalTransactionState方法中檢查DB事務狀態,如果成功,則回覆COMMIT_MESSAGE,否則回覆ROLLBACK_MESSAGE