解密分散式事務框架-Fescar

咖啡拿鐵發表於2019-03-28

1.分散式事務

在去年的時候我寫過一篇關於分散式事務的文章再有人問你分散式事務,把這篇扔給他。再這篇文章中我叫大家能不用分散式事務就別用分散式事務,因為會引入很多的複雜度。當時說這個的時候其實還有一個原因,沒有大廠的成熟開源解決方案,雖然再網上有很多開源的分散式事務框架,但是都不是太成熟,沒有大量的業務驗證。它不像其他的分散式中介軟體有大量的成熟的解決方案,比如分散式訊息佇列中介軟體:Apache Kafka,Apache RocketMQ,Apache Pulsar這三個均是Apache頂級專案;又比如分散式任務排程,也有很多的開源比如XXL-JOB,Elastic-Job都是有很多的公司在使用。

而我們的分散式事務框架,卻一直沒有一個經過大量業務驗證的框架。通過我的一些瞭解,其實各大公司都是有自己的分散式事務的解決方案,但很多時候都和業務上強耦合了,不適於做一些通用的框架。但是阿里在今年年初的時候給了大家一個驚喜,Fescar開源!從而讓大家再以後選擇分散式事務的時候多了一個選擇方案,而且是經過成熟業務驗證的方案。

這篇文章我會帶大家認識Fescar,以及他的一些設計。後續的文章還會陸續推出Fescar的核心原始碼解析篇。

2.初識Fescar

說Fescar之前這裡先簡單的介紹一下著名的2PC:XA Transactions。 在XA協議中分為兩階段:

解密分散式事務框架-Fescar
第一階段:事務管理器要求每個涉及到事務的資料庫預提交(precommit)此操作,並反映是否可以提交.

第二階段:事務協調器要求每個資料庫提交資料,或者回滾資料。

優點: 儘量保證了資料的強一致,實現成本較低,在各大主流資料庫都有自己實現,對於MySQL是從5.5開始支援。

缺點:

  • 單點問題:事務管理器在整個流程中扮演的角色很關鍵,如果其當機,比如在第一階段已經完成,在第二階段正準備提交的時候事務管理器當機,資源管理器就會一直阻塞,導致資料庫無法使用。
  • 同步阻塞:在準備就緒之後,資源管理器中的資源一直處於阻塞,直到提交完成,釋放資源。
  • 資料不一致:兩階段提交協議雖然為分散式資料強一致性所設計,但仍然存在資料不一致性的可能,比如在第二階段中,假設協調者發出了事務commit的通知,但是因為網路問題該通知僅被一部分參與者所收到並執行了commit操作,其餘的參與者則因為沒有收到通知一直處於阻塞狀態,這時候就產生了資料的不一致性。
  • 只能用於單一型別資料庫,跨資料庫種類,其他快取或者檔案等資源不支援。

總的來說,XA協議比較簡單,成本較低,但是其單點問題,以及不能支援高併發(由於同步阻塞)依然是其最大的弱點。

可以看見XA協議問題較多所以其作為分散式事務方案討論的時候基本都會被無情的幹掉,但是XA有個很好優點是對業務沒有侵入(資料庫層面),再業務上不需要編寫額外的程式碼,更加關注業務。

在我的那篇文章中我又介紹了幾種在應用層面上的分散式事務,但是或多或少的對業務都有一定的入侵。比如我們的TCC,常用的一些TCC框架都需要編寫try,confirm,cancel等介面對於現成的業務需要進行轉換。如果採用本地訊息表模式那麼又需要增加額外的表。如果採用事務性訊息比如RocketMQ,會讓一些沒有使用該訊息佇列的業務需要更換訊息佇列。如果採用Saga模式,同樣我們需要編寫正向和反向介面。可以看見不論採用哪種分散式事務方案,都會有一定的業務改造,業務入侵成本。

而Fescar結合了XA的無侵入的優點和其他應用層事務協議高效能的優點,在應用層實現了二階段協議的事務,同時對業務程式碼基本無侵入。

2.1 Fescar和XA

Fescar雖然是二階段提交協議的分散式事務,但是其解決了上面XA的一些缺點:

  • 單點問題:雖然目前Fescar(0.4.1)還是單server的,但是Fescar官方預計將會在0.5.x中推出HA-Cluster,到時候就可以解決單點問題。
  • 同步阻塞:Fescar的二階段,其再第一階段的時候本地事務就已經提交釋放資源了,不會像XA會再兩個prepare和commit階段資源都鎖住,並且Fescar,commit是非同步操作,也是提升效能的一大關鍵。
  • 資料不一致:如果出現部分commit失敗,那麼fescar-server會根據當前的事務模式和分支事務的返回狀態的結果來進行不同的重試策略。並且fescar的本地事務會在一階段的時候進行提交,其實單看資料庫來說在commit的時候資料庫已經是一致的了。
  • 只能用於單一資料庫: Fescar提供了兩種模式,AT和MT。在AT模式下事務資源可以是任何支援ACID的資料庫,在MT模式下事務資源沒有限制,可以是快取,可以是檔案,可以是其他的等等。當然這兩個模式也可以混用。

同時Fescar也保留了接近0業務入侵的優點,只需要簡單的配置Fescar的資料代理和加個註解,加一個Undolog表,就可以達到我們想要的目的。

3.Fescar總體設計

Fescar的設計核心就是他的角色分類。不論是資料庫上的XA還是Fescar都有兩個角色TM(事務管理器)和RM(資源管理器),同時Fescar還有一個TC(事務協調器)。我們先來看看如果沒有TC,只有TM和RM會發生什麼呢?這裡我舉個簡單的例子,小明去網站上面購買了一個商品,如下圖所示:

解密分散式事務框架-Fescar

這裡小明其實就是TM(事務管理器),而商品和賬戶其實就是我們的RM(資源管理器),正常情況下可能沒問什麼問題,賬戶和庫存都能扣減成功。如果小明再扣減庫存的時候成功但是在扣減賬戶的時候失敗,這個時候就需要對我們的庫存資源進行回滾。小明這個時候就會通知庫存把上個階段扣減的貨物補回來。但是回滾庫存的時候庫存服務不穩定,這次回滾就失敗了。一般來說小明會不斷的去重試,直到成功。這樣就有個問題小明就一直被阻塞,不能做任何事。這個也可以看做二階段commit/rollback的時候一直會阻塞TM,網易DDB的XA協議針對這種情況會做一個非同步執行緒的操作。但是在Fescar中一切都是由TC去做的,當然TC其實不僅僅會做二階段失敗的重試,他會做二階段的所有RM的commit和rollback,讓我們的TM做更少的事。

再Fescar中TM,RM,TC的關係如下面官方提供的圖:

解密分散式事務框架-Fescar

  • TM:事務的發起者。用來告訴TC,全域性事務的開始,提交,回滾。
  • RM:具體的事務資源,每一個RM都會作為一個分支事務註冊在TC。
  • TC:事務的協調者。也可以看做是Fescar-servr,用於接收我們的事務的註冊,提交和回滾。

這三個角色的分工明確,正是我們Fescar真正的核心所在,下面我會通過如何使用Fescar帶大家更加深刻的理解這三個角色。

4.快速開始Fescar

在Fescar的github上已經提供了一個簡單的例子https://github.com/fescar-group/fescar-samples,這裡我們需要將這個例子下載下來。

4.1搭建TC Fescar-server

首先我們搭建事務協調器,也可以叫做搭建Fescar-Server.官網的例子是使用Nacos加上fescar-server已經打好的Jar包執行的。這裡為了方便我們直接下載fescar的程式碼https://github.com/alibaba/fescar。

找到我們的Server:

解密分散式事務框架-Fescar

直接執行main方法,該方法會幫助我們在本地啟動一個埠號為8091的fescar-server服務。如果我們想要進行服務註冊,我們可以修改registry.conf下面的type

解密分散式事務框架-Fescar

可以看見在0.4.1版本的時候支援四種服務註冊,nacos,eureka,redis,zk。目前使用redis進行服務註冊是有問題的,我也提了一個PR給官方進行修正。當然為了方便其實選擇file,後續我們直連是最為便捷的。

再執行main方法之後,如果出現Server started日誌,就代表我們的TC(事務協調器)成功啟動。

解密分散式事務框架-Fescar

4.2 認識TM

上面事務協調器已經搭建完成,我們接下來需要做的就是將TM和RM執行起來,將對應的操作交給我們的事務協調器去做。這個時候我們需要開啟fescar-samples這個專案:由於這個專案使用的RPC是Dubbo,他預設配的服務註冊中心是Nacos,需要我們再本地安裝一個Nacos,具體安裝可以自行搜尋,這裡不展開講了。

這裡官方例子中,業務關係如下圖:

解密分散式事務框架-Fescar

可以看見Business也就是我們的TM,找到對應的程式碼:

解密分散式事務框架-Fescar

從程式碼中我們知道啟動一個分散式事務是需要新增@GlobalTransactional註解的,當然Fescar也提供了API的方式讓我們達到同樣的效果。我們同時也需要修改registry.conf中的Type為file。

如果不是在例子中,而是在我們真正的業務上完全不需要修改業務程式碼,直接在我們分散式事務發起方新增上這個註解即可。

這個GloablTranscational註解到底做了什麼呢?其實加了這個註解的都會走一個叫GlobalTransactionalInterceptor的切面,再這個切面中又會進入TrascationTemplate這個類中的excute方法,這個也是TM的核心方法:

解密分散式事務框架-Fescar

上面的程式碼有部分刪減,只選取了核心的流程。TrascationTemplate其實也是Fescar提供給我們的API,如果不使用註解那麼我們也可以模仿他的方式去做。可以看見主要分為五步:

  1. 獲取當前上下文中是否已經有事務,這一步是通過ThreadLocal去實現的,如果有則獲取當前的,如果沒有則獲取預設的。
  2. 開啟事務,這一步是向TC(事務協調器)發出一個請求,註冊一個GloabTranscation,這裡的timeout是指超過這段時間沒有rollback或者commit,TC會幫助我們做rollback。
  3. 做業務。
  4. 如果該方法丟擲了異常,則回滾。這裡要注意的時候丟擲異常,很多時候我們會把異常給捕獲,導致我們這裡根本沒有異常丟擲,所以就不會出現回滾。這裡的回滾也是向TC發起一個回滾請求,由他幫助我們對RM進行回滾。
  5. 如果沒有異常則向TC發起commit,TC會幫助我們向RM非同步發起提交請求。

TM的核心過程主要是這5步,其他詳細的講解會在後續的程式碼中體現。

4.3 認識RM

當我們上面的Business發起業務請求之後,就來到了我們RM的流程,我們的Storage和Order服務是怎麼知道現在已經是處於分散式事務當中了呢?這個就需要藉助RPC框架來完成了,這裡我們使用的是Dubbo,fescar為dubbo提供了一個filter,如下圖所示:

解密分散式事務框架-Fescar

這裡會從rpcContext中獲取我們的xid,也就是我們的分散式事務ID,如果有的話就證明本次請求處於分散式事務中,那麼就會把XID種入我們的RootContext(fescar的本地上下文)。如果你不是Dubbo,那麼也可以根據此方法適配你的RPC。

在RM中我們應該做什麼呢?只需要做下面兩步:

  1. 將資料來源換成Fescar代理
    解密分散式事務框架-Fescar
  2. 在當前資料庫中新增一個Undolog的表,用於記錄日誌回滾。

再Fescar中不僅僅是對dataSource進行代理,也會對connection和statement進行代理,如下圖:

解密分散式事務框架-Fescar

大家都知道我們的SQL的具體執行需要依賴Statement,在Fescar的StatementProxy中有如下程式碼:

解密分散式事務框架-Fescar

可以看見執行的方法是ExecuteTemplate.execute,在execute方法中會根據我們執行語句的型別記錄我們的Undolog,具體的執行流程參考下面官方的一張圖片:

解密分散式事務框架-Fescar

總的來說我們的RM核心流程主要有兩個:一個是如何識別分散式事務,另外一個是通過我們資料來源代理讓我們原本簡單的執行SQL流程做了更多的事。

5.總結

寫這篇文章的目的,不僅僅是讓大家知道Fescar,也是讓更多的人知道一個優秀分散式事務框架到底應該怎樣去做。目前Fescar的版本號是0.4.1,還有很多功能比如HA-Cluster,SpringCloud整合還沒有釋出。所以目前再線上使用的話可能會遇到Fescar單點的問題,所以目前還不是太推薦線上使用。Fescar的目前規劃會在0.5.x版本推出HA-Cluster,到時候其單點問題就會被解決。

這篇文章的原理目前介紹的比較粗淺,後面會陸續推出三篇文章詳細介紹分析:TC,TM,RM,敬請期待。

最後這篇文章被我收錄於JGrowing-Java分散式事務篇,一個全面,優秀,由社群一起共建的Java學習路線,如果您想參與開源專案的維護,可以一起共建,github地址為:github.com/javagrowing… 麻煩給個小星星喲。

如果大家覺得這篇文章對你有幫助,你的關注和轉發是對我最大的支援,O(∩_∩)O:

解密分散式事務框架-Fescar

參考文章

阿里開源分散式事務解決方案Fescar: mp.weixin.qq.com/s/TFGRcHV6E…

相關文章