您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關Spring中如何實現分布式事務,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
那么在分布式系統當中,我們應該怎么樣去實現事務呢?這就需要從分布式系統的原則說起。分布式系統的實現原則有幾種說法,如BASE原理、ACP原理。
其中ACP是:
A: 可用性(Availability)
C: 一致性(Consistency)
P: 分區容錯性(Tolerance of network Partition)
A和P沒什么好說的,就是分布式系統的基本特性,C(一致性)就是指在分布式系統當中,多個節點之間數據的一致性,包括一個節點修改的數據,通過另一個節點訪問的時候也能看到;以及當一個操作需要修改多個數據源的數據的時候,多個修改要都能夠完成,或者都不完成。
這里的一致性,我們可以看做是上面說的數據庫事務的ACID特性中,原子性、一致性,甚至是隔離性的統一。如果以ACID這4個特性為要求來實現分布式系統,在現實當中是不可能的,其中原子性就沒有辦法實現。如果一個業務請求,要修改多個數據庫中的數據,那么這多個數據庫的操作,就無法實現原子性,勢必會有一個先后,在第一個數據庫上完成以后,再在第二個數據庫上完成,那么這期間的一點點時間,就違反了原子性。
所以,我們往往無法在分布式系統中實現完全的一致性,所以就有了BASE理論。BASE是Basically Available(基本可用)、Soft state(軟狀態)和Eventually consistent(最終一致性)三個短語的縮寫。BASE理論是對CAP中一致性和可用性權衡的結果,要求實現最終一致性即可。
其中,Soft state(軟狀態)是指,在一個業務操作過程中,允許出現一個中間狀態,也就是軟狀態,而不要求原子性那樣,要么都完成,要么都不完成。例如在下單的時候,出現一個“正在處理”的狀態。由于有這個軟狀態,那我的一致性,就不要求是強一致性,而是最終一致性,也就是說,只要最終這個請求能處理完,所有的數據狀態都是處理完的狀態;如果期間出錯了,所有的數據也都一致,該失敗的失敗、該退錢的退錢、該重置的重置。
所以,確定了分布式系統的實現原則是最終一致性以后,同時也明確了我們實現分布式事務的原則,也是最終一致性。
其實,不管是數據庫事務的ACID特性,還是分布式事務的最終一致性,其實,都是根據事務的定義和它的兩個目標,所采取的不同的實現方式。
那么我們應該怎么實現這個最終一致性呢?
首先,任何一個分布式系統,總是由一個個的系統組成,也就是一個個的服務,這些服務又可以部署多個。同時,我們的整個系統也需要一定的方式相互作用、相關通信。有時候,我們可以讓一個服務直接調用另一個服務的接口(如果有提供的話);還有時候,我們可以讓兩個服務通過一個MQ之類的消息中間件通信,共同完成一些業務。但是,無論如何,大部分情況下,分布式系統的一個服務總是會訪問多個數據源。最典型的例子就是通過MQ接受一個事件,然后出發一些操作,再把結果發送到另一個隊列里。
對于這種每個服務訪問多個數據源的情況,其實就是一個最簡單的分布式事務的場景。如果大家在網上搜“Spring分布式事務實現”,搜到的結果也都是在說這個場景下的分布式事務實現過程。
要實現這個事務,首先需要對Spring的事物機制有一定了解。對于這種情況,最簡單的就是使用Spring的JTA事務管理。但是,我們知道,JTA事務管理是通過兩階段提交實現的,在很多情況下,它的效率是很低的。因為它在多個數據源修改數據的時候,這些數據一直都處在被鎖的狀態,知道多個數據源的事務都提交完成,才會釋放。
如果不用JTA,Spring也給我們提供了幾種方式,來近似的實現分布式事務(注意這里說的近似)。例如:
事務同步,也就是提交一個事物的時候,通過Listener等方式通知另一個事務也提交。但是這種情況下,如果第二個事務提交的時候出錯了,第一個事物就無法回滾,因為他已經提交完成了。
鏈式事務,就是將多個事務,包裝在一個鏈式事務管理器當中,在提交事務的時候,一次提交里面的事務。對于這種實現,也存在上面說的問題。
還有其他的一些方式,就不過多說明。
所以,使用Spring在單服務多數據源的情況下,實現分布式事務,實際上沒辦法完全實現事務的,因為出錯的時候不能保證都會滾。那么這時候,就需要再通過其他機制來補充。
首先就是重試,也就是在出錯的時候,重試之前的操作。這在有MQ的時候比較常用,因為一般的MQ服務器,在你讀消息以后,處理的時候如果出錯了,那么這個讀消息的操作不會被提交。那這個消息就會被重新讀到,重新出發剛才的操作。這時候,我們就需要考慮這個方法的冪等性,保證在重復消息的時候不會重復處理數據。
其次,我們需要自己處理一些錯誤。例如上面的情況,重試幾次以后,一直沒有成功,那么這時候就需要走失敗邏輯。有時候,我們也可以通過一個定時器來檢查一定時間內沒有完成的失敗操作。
有些情況下,我們還需要考慮其他各種錯誤,如網絡錯誤、超時,系統宕機等等。
大家可以試想一下,分布式系統越復雜,它的各種出錯的情況就越多,我們需要考慮的補救措施就越多。那這種修修補補的實現分布式事務的最終一致性的做法,始終不是一個好的辦法。但是,使用Spring解決單服務的分布式系統,始終是分布式事務實現的基礎。我們可以用其他的模式來方便我們解決分布式事務,但是在每個服務當中,我們還是要經常使用事務同步、鏈式事務等,來實現事務。我們用Spring來保證絕大多數情況下的事務問題,而對于特殊的錯誤情況,就采用其他的模式來解決。
剛才說了我們用其他模式來覺得分布式事務問題,那么都有什么模式呢?
消息驅動模式是,當某個業務請求需要由多個服務參與完成的時候,這些服務之前不直接通信,而是通過一個MQ中間件來通信。比如對于一個訂單支付的請求,接收到支付完成的請求后,通過MQ,通知訂單服務去完成訂單,訂單服務再去通知商品服務去減庫存,再通知物流服務去發起物流流程。
那么,對于每一個服務來說,都需要先從一個隊列讀取一個消息,完成自己的業務操作,再往另一個隊列發送一個消息,這就需要操作一個數據和一個MQ服務器。這也就是上面說的單服務的分布式事務實現。對于這種模式而言,我們用事務同步保證在每個服務中,在大部分情況下都能保證事務。即使偶爾出現網絡錯誤、系統錯誤等,通過重試就能解決大部分問題。如果重試一直不能解決,那就再處理失敗邏輯。
我們使用這種方式,最重要的,就是對這個消息、和他的處理流程的編排,其次,它也是一種響應式的編程思維。
Event Sourcing在上面說的消息驅動的基礎上,進一步提升事件(也就是之前的消息)的地位,讓它成為系統的一等公民。也就是說,怎么的系統不是基于原先那些實體的,而是基于事件的,一個事件就代表一個業務操作和業務數據狀態的更改。至于業務數據,我們不需要把它保存在數據庫中,即使保存,也只是為了查詢數據方便而保存。
在Event Sourcing模式中,每個服務完成某個邏輯的方式,跟上面說的消息驅動模式差不多,就是對于用戶的每個操作,會產生一個事件(可能多個),這個事件會被某個服務的某個處理方法處理,它也有可能再產生其他的事件,再由其他服務處理,直到完成整個業務流程。但是,它跟消息驅動的最大區別就是,在Event Sourcing的服務里,業務狀態數據不一定要保存在數據庫中,就算保存,出錯了也沒關系,反正它可以根據Event事件重新生成。所以這個地方的事務,我們只需要保證Event保存成功即可。當然,我們需要其他的機制,方便我們能夠重新生成業務數據,而這,一般都是實現Event Sourcing的框架來提供。
除了上面說的通過一個中間價關聯不同的服務,在有些分布式系統當中,我們的不同的服務可以直接通信,例如Spring Cloud微服務框架就提供Rest方式訪問別的服務。那么這時候,就相當于,我的一個服務除了訪問自己的數據庫以外,還要訪問別的服務,這里的這個服務就可以當做是一個數據庫。我們可能要調用別的服務的某個接口完成一些業務。
在這種情況下,一個服務提供的接口,不可能實現事務,也就是先操作數據,再Commit,如果出錯了再Rollback。但是,我們可以借鑒事務的這種處理思路,來自己提供類似事務的方法,這就是TCC模式。一個事物是通過Do-Commit/Rollback來實現的,在TCC模式中,是通過給每一個服務間調用的操作接口,提供一套Try-Confirm/Cancel接口。
還是舉一個例子,就是用戶下單以后支付完成。支付完成的時候由先訂單服務處理,然后調用商品服務去減庫存。大家用Spring Cloud的話,可能就在商品服務里寫一個接口直接做減庫存的操作,但是在TCC模式下,我們需要3個接口。首先是減庫存的Try接口,在這里,我們要檢查業務數據的狀態、檢查商品庫存夠不夠,然后做資源的預留,也就是在某個字段上設置預留的狀態。然后在Confirm接口里,完成庫存減1的操作。在Cancel接口里,把之前預留的字段重置。
這可能聽著有點繁瑣,感覺可以一次完成的事情,為什么要分成2步,首先這么做是為了能夠在出錯的時候正確的重置庫存數據,其次這個預留操作跟Confirm操作是兩個請求,中間可能會有其他并發請求。從理論上說,只要我們在Try接口里面預留資源的邏輯是正確的,那么,即使Confirm的時候出錯了,我也可以通過重試Confirm請求來完成
這其實不是一種模式,只是一種方式。例如在TCC模式下,在準備調用Confirm接口的時候,目標服務突然宕機了,或者發起請求的服務突然宕機或出錯了,導致這個Confirm請求一直沒有被調用。那么,在系統恢復以后,我該怎么完成之前的事務呢?除了上面說的用定時器定期檢查未完成的操作以外(需要能夠通過某種數據狀態判斷業務沒有執行完成后),我們還可以用數據庫來記錄事務的運行狀態。
例如在TCC模式中,每當一個服務A要使用TCC模式調用另一個服務B的時候,服務A將這個TCC的事務狀態寫到數據庫中,根據具體實現,可能是在調用前記錄當前事務的狀態,調用完成再保存該調用的參數和結果狀態,這個事務完成以后(也就是調用完Confirm,或Cancel以后),再更新成完成的狀態。那么,通過合理的設計,我們就能在各種出錯情況下,保證能繼續完成這個事務,或取消這個事務。
以上就是Spring中如何實現分布式事務,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。