Fescar是阿里18年開源的分佈式事務的框架。Fescar的開源對分佈式事務框架領域影響很大。做爲開源大戶,Fescar來自阿里的GTS,經歷了好幾回雙十一的考驗,一經開源便頗受關注。今天就來看了Fescar的代碼,看看究竟是怎麼一回事。java
在XA協議中分爲兩階段: git
第一階段:事務管理器要求每一個涉及到事務的數據庫預提交(precommit)此操做,並反映是否能夠提交.github
第二階段:事務協調器要求每一個數據庫提交數據,或者回滾數據。web
優勢: 儘可能保證了數據的強一致,實現成本較低,在各大主流數據庫都有本身實現,對於MySQL是從5.5開始支持。spring
缺點sql
一、同步阻塞問題。執行過程當中,全部參與節點都是事務阻塞型的。當參與者佔有公共資源時,其餘第三方節點訪問公共資源不得不處於阻塞狀態。數據庫
二、單點故障。因爲協調者的重要性,一旦協調者發生故障。參與者會一直阻塞下去。尤爲在第二階段,協調者發生故障,那麼全部的參與者還都處於鎖定事務資源的狀態中,而沒法繼續完成事務操做。(若是是協調者掛掉,能夠從新選舉一個協調者,可是沒法解決由於協調者宕機致使的參與者處於阻塞狀態的問題)api
三、數據不一致。在二階段提交的階段二中,當協調者向參與者發送commit請求以後,發生了局部網絡異常或者在發送commit請求過程當中協調者發生了故障,這回致使只有一部分參與者接受到了commit請求。而在這部分參與者接到commit請求以後就會執行commit操做。可是其餘部分未接到commit請求的機器則沒法執行事務提交。因而整個分佈式系統便出現了數據部一致性的現象。緩存
四、二階段沒法解決的問題:協調者再發出commit消息以後宕機,而惟一接收到這條消息的參與者同時也宕機了。那麼即便協調者經過選舉協議產生了新的協調者,這條事務的狀態也是不肯定的,沒人知道事務是否被已經提交。網絡
Fescar雖然是二階段提交協議的分佈式事務,可是其解決了上面XA的一些缺點:
同時Fescar也保留了接近0業務入侵的優勢,只須要簡單的配置Fescar的數據代理和加個註解,加一個Undolog表,就能夠達到咱們想要的目的。
這裏直接下載 fescar-samples
項目的readme寫的很詳細
準備工做 執行sql/all_in_one.sql 下載0.4.1版本server 客戶端與服務端版本號保持一致 啓動fescar server sh fescar-server.sh 8091 ../data/ 啓動business、storage、account、order 數據庫默認鏈接127.0.0.1:3306,不一樣的注意修改 事務成功 GET http://127.0.0.1:8084/purchase/commit 事務回滾 GET http://127.0.0.1:8084/purchase/rollback
看了下項目的依賴和配置文件,和常規的項目相比,多了下面幾個依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-alibaba-fescar</artifactId> <version>2.1.0.BUILD-SNAPSHOT</version> <exclusions> <exclusion> <groupId>com.alibaba.fescar</groupId> <artifactId>fescar-spring</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.alibaba.fescar</groupId> <artifactId>fescar-spring</artifactId> <version>0.4.1</version> </dependency>
啓動相應的項目,觀察相應的數據變化,這個demo算是跑起來
Fescar將一個本地事務作爲一個分佈式事務分支,因此若干個分佈在不一樣微服務中的本地事務共同組成了一個全局事務,結構以下。
觀察整個demo項目,惟一不一樣的就是多了個註解 @GlobalTransactional 因此我先從這個註解入手
在 fescar-spring 包中發現了 com.alibaba.fescar.spring.annotation.GlobalTransactionalInterceptor這個類,這個類實現了org.aopalliance.intercept.MethodInterceptor
已動態代理的方式提供了對GlobalTransactional 註解方法的支持
最終咱們的邏輯會到 com.alibaba.fescar.tm.api.TransactionalTemplate#execute方法中
execute方法是事務一階段的核心,主要作了如下幾件事情
1.構造一個全局事務
2.開始事務
經過 TmRpcClient 請求TC server端,註冊一個全局的事物。獲得一個全局事務id:XID,將這個XID設置到上下文中
3.執行原始的業務代碼
在springcloud中咱們用feign去請求其餘服務,fescar對feign進行了重寫,在org.springframework.cloud.alibaba.fescar.feign.FescarFeignClient中fescar對每一個feign的請求都會作一次判斷,若是在全局上下文中含有事務id,feign的請求頭會帶上XID
4.事務的回滾或者提交
提交和回滾本質上都是構造一個請求,請求遠程的TC server端
5.清除ThreadLocal中的鉤子函數,咱們暫時還沒用到。
參與者在收到feign請求時,首先會被org.springframework.cloud.alibaba.fescar.web.FescarHandlerInterceptor攔截,攔截器會把請求中的XID設置到本地的全局上下文中
而後參與者就開始執行本地的業務。fesca在這裏作了大量的背後工做。爲了能在jdbc鏈接中添加了本身的邏輯,fesca重寫了Statment以前的邏輯
JdbcTemplate數據源被配置成了Fescar實現DataSourceProxy,進而控制了後續的數據庫鏈接使用的是Fescar提供的ConnectionProxy,Statment使用的是Fescar實現的StatmentProxy,最終Fescar就瓜熟蒂落地實現了在本地事務執行先後增長所須要的邏輯
獲取代理數據源
獲取代理鏈接
獲取代理statement
在PreparedStatementProxy中,真正的執行sql的邏輯被放在了ExecuteTemplate中
根據sqltype,這裏有多個executor,感受和mybatis的代碼風格有點類似
這裏咱們已UpdateExecutor爲例
executor.execute(args);
這行代碼入口在com.alibaba.fescar.rm.datasource.exec.BaseTransactionalExecutor#execute
將ConnectionProxy與Xid(事務ID)進行綁定,這樣後續判斷當前本地事務是否處理全局事務中只須要看ConnectionProxy中Xid是否爲空
再執行com.alibaba.fescar.rm.datasource.exec.AbstractDMLBaseExecutor#doExecute
這裏會判斷是不是自動提交
若是是自動提交,就會先設置爲爲非自動提交再執行executeAutoCommitFalse
這裏 executeAutoCommitFalse方法是整個邏輯的核心
先查詢Update前對應行記錄的快照beforeImage,再執行Update語句,完成後再查詢Update後對應行記錄的快照afterImage,最後將beforeImage、afterImage生成UndoLog追加到Connection上下文ConnectionContext中
若是一切順利,最終咱們會執行本地事務的提交,即執行com.alibaba.fescar.rm.datasource.ConnectionProxy#commit方法,最終邏輯會到com.alibaba.fescar.rm.datasource.ConnectionProxy#processGlobalTransactionCommit
邏輯以下
1.註冊分支事務到TC server端
2再將ConnectionContext中的UndoLog寫入到undo_log表中
3而後調用targetConnection對本地事務進行commit,將UndoLog與業務SQL一塊兒提交
4最後上報分支事務的狀態(成功 or 失敗),並將ConnectionContext上下文重置
事務的參與者便完成了本身的邏輯。二階段中的一階段的邏輯即是上述代碼,這裏再引用一張官方的流程圖