前言:筆者最近實現了基於可靠消息方案的分佈式事務:Lottor。本文將會介紹Lottor的概況,在後續系列文章介紹具體的實現,歡迎關注。html
分佈式事務是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位於不一樣的分佈式系統的不一樣節點之上。算法
首先,解釋下事務的概念:一組操做要麼都完成以後提交,要麼所有回滾。分佈式事務特指在分佈式環境下,一次事務設計多個服務進程,說白了就是跨進程的事務,這樣就不能控制事務組的一致性。數據庫
分佈式系統區別於傳統的單體應用,單體應用的服務模塊和數據都在一個服務中,使用Spring框架的事務管理器便可知足事務的屬性。而分佈式系統中,來自客戶端的一次請求每每涉及多個服務,事務的一致性問題由此產生。服務器
CAP定理是由加州大學伯克利分校Eric Brewer教授提出來的,他指出WEB服務沒法同時知足一下3個屬性:微信
具體地講在分佈式系統中,在任何數據庫設計中,一個Web應用至多隻能同時支持上面的兩個屬性。網絡
上面這句話的表述,不少人都用過,是的,這是一種誤解。注意CAP定律的完整表述:Any networked shared-data system can have at most two of the three desired properties.架構
CAP 定律的前提是 P,當 P 決定後纔有 CA 的抉擇。所以,簡單粗暴地說「三選二」是有必定誤導性的。併發
在分佈式系統中,咱們每每追求的是可用性,它的重要程序比一致性要高,那麼如何實現高可用性呢? 前人已經給咱們提出來了另一個理論,就是BASE理論,它是用來對CAP定理進行進一步擴充的。BASE理論指的是:框架
BASE理論是對CAP中的一致性和可用性進行一個權衡的結果,理論的核心思想就是:咱們沒法作到強一致,但每一個應用均可以根據自身的業務特色,採用適當的方式來使系統達到最終一致性(Eventual consistency)。異步
功能需求最主要的是知足分佈式事務的一致性,涉及的事務組中的操做爲多個寫操做,當產生一個或多個寫操做失敗時,回滾整個事務組中的操做。
X/Open 組織(即如今的 Open Group )定義了分佈式事務處理模型。 X/Open DTP 模型( 1994 )包括應用程序( AP )、事務管理器( TM )、資源管理器( RM )、通訊資源管理器( CRM )四部分。
XA 就是 X/Open DTP 定義的交易中間件與數據庫之間的接口規範(即接口函數),交易中間件用它來通知數據庫事務的開始、結束以及提交、回滾等。 XA 接口函數由數據庫廠商提供。
二階段提交的算法思路能夠歸納爲:參與者將操做成敗通知協調者,再由協調者根據全部參與者的反饋情報決定各參與者是否要提交操做仍是停止操做。第一階段:準備階段(投票階段)和第二階段:提交階段(執行階段)。
三階段提交(Three-phase commit),也叫三階段提交協議(Three-phase commit protocol),是二階段提交(2PC)的改進版本。
若是由於協調者或網絡問題,致使參與者遲遲不能收到來自協調者的commit或rollback請求,那麼參與者將不會如兩階段提交中那樣陷入阻塞,而是等待超時後繼續commit。相對於兩階段提交雖然下降了同步阻塞,但仍然沒法避免數據的不一致性。在分佈式數據庫中,若是指望達到數據的強一致性,那麼服務基本沒有可用性可言,這也是爲何許多分佈式數據庫提供了跨庫事務,但也只是個擺設的緣由,在實際應用中咱們更多追求的是數據的弱一致性或最終一致性,爲了強一致性而丟棄可用性是不可取的。
根據BASE理論,系統並不保證續進程或者線程的訪問都會返回最新的更新過的值。系統在數據寫入成功以後,不承諾當即能夠讀到最新寫入的值,也不會具體的承諾多久以後能夠讀到。
弱一致性的特定形式。系統保證在沒有後續更新的前提下,系統最終返回上一次更新操做的值。在沒有故障發生的前提下,不一致窗口的時間主要受通訊延遲,系統負載和複製副本的個數影響。DNS 是一個典型的最終一致性系統。 在工程實踐上,爲了保障系統的可用性,互聯網系統大多將強一致性需求轉換成最終一致性的需求,並經過系統執行冪等性的保證,保證數據的最終一致性。但在電商等場景中,對於數據一致性的解決方法和常見的互聯網系統(如 MySQL 主從同步)又有必定區別。
TCC 其實就是採用的補償機制,其核心思想是:針對每一個操做,都要註冊一個與其對應的確認和補償(撤銷)操做。它分爲三個階段:
TCC與2PC協議比較:
相似於可靠消息方案。
消息生產方,須要額外建一個消息表,並記錄消息發送狀態。消息表和業務數據要在一個事務裏提交,也就是說他們要在一個數據庫裏面。而後消息會通過MQ發送到消息的消費方。若是消息發送失敗,會進行重試發送。
消息消費方,須要處理這個消息,並完成本身的業務邏輯。此時若是本地事務處理成功,代表已經處理成功了,若是處理失敗,那麼就會重試執行。若是是業務上面的失敗,能夠給生產方發送一個業務補償消息,通知生產方進行回滾等操做。
生產方和消費方定時掃描本地消息表,把還沒處理完成的消息或者失敗的消息再發送一遍。若是有靠譜的自動對帳補帳邏輯,這種方案仍是很是實用的。
這種方案遵循BASE理論,採用的是最終一致性,即不會出現像2PC那樣複雜的實現(當調用鏈很長的時候,2PC的可用性是很是低的),也不會像TCC那樣可能出現確認或者回滾不了的狀況。
優勢: 一種很是經典的實現,避免了分佈式事務,實現了最終一致性。
缺點: 消息表會耦合到業務系統中,若是沒有封裝好的解決方案,會有不少雜活須要處理。
RocketMQ第一階段發送Prepared消息時,會拿到消息的地址,第二階段執行本地事物,第三階段經過第一階段拿到的地址去訪問消息,並修改消息的狀態。
若是確認消息發送失敗了怎麼辦?RocketMQ會按期掃描消息集羣中的事務消息,若是發現了Prepared消息,它會向消息發送端(生產者)確認,Bob的錢究竟是減了仍是沒減呢?若是減了是回滾仍是繼續發送確認消息呢?RocketMQ會根據發送端設置的策略來決定是回滾仍是繼續發送確認消息。這樣就保證了消息發送與本地事務同時成功或同時失敗。
若是endTransaction方法執行失敗,數據沒有發送到broker,致使事務消息的 狀態更新失敗,broker會有回查線程定時(默認1分鐘)掃描每一個存儲事務狀態的表格文件,若是是已經提交或者回滾的消息直接跳過,若是是prepared狀態則會向Producer發起CheckTransaction請求,Producer會調用DefaultMQProducerImpl.checkTransactionState()方法來處理broker的定時回調請求,而checkTransactionState會調用咱們的事務設置的決斷方法來決定是回滾事務仍是繼續執行,最後調用endTransactionOneway讓broker來更新消息的最終狀態。
消費失敗
解決超時問題的思路就是一直重試,直到消費端消費消息成功
消費超時
消費失敗怎麼辦?阿里提供給咱們的解決方法是:人工解決。你們能夠考慮一下,按照事務的流程,由於某種緣由Smith加款失敗,那麼須要回滾整個流程。若是消息系統要實現這個回滾流程的話,系統複雜度將大大提高,且很容易出現Bug,估計出現Bug的機率會比消費失敗的機率大不少。這也是RocketMQ目前暫時沒有解決這個問題的緣由,在設計實現消息系統時,咱們須要衡量是否值得花這麼大的代價來解決這樣一個出現機率很是小的問題,這也是你們在解決疑難問題時須要多多思考的地方。
Lottor用於解決微服務架構下分佈式事務的問題,基於可靠性消息事務模型實現。
Lottor由三部分組成:
Lottor服務器與客戶端之間的通訊使用的高性能通訊框架:Netty。全部的客戶端(生產端和消費端)都會與服務器保持長鏈接。Lottor UI用於展現系統中的事務組詳細信息,包括預提交的事務組、消費失敗的事務消息,並支持頁面操做失敗的消息(如補償或重試)。
生產方分爲三步:
預發送
。Lottor Server:
pre-commit
。unconsumed
。不然,回滾狀態只會修改事務組狀態(按期刪除)。pre-commit
的事務組消息,Lottor Server將會按期回查生產方。unconsumed
(通常4h),Lottor Server將會按期回查消費方。消費方:
Lottor 客戶端的持久化,提供了SPI接口,可經過配置動態指定。目前支持:JDBC、Redis、MongoDB和文件系統。
這裏所說的告警機制及消費補償是針對消費端,可靠消息方案是保證了事務消息必定可以到達消費方,可是消費方可能由於某些緣由而沒法成功消費,有些消費異常是能夠經過重試解決的,而有些異常是須要告警以後人工干預的。好比消費方暫時不可用,或者是多個消費方消費的順序問題,能夠經過定時的重試機制完成。而若是是因爲生產方發送的事務消息出錯(參數構造錯誤),此時消費方已經提交了本地事務組,因此是沒法經過重試實現成功消費,致使須要告警,人爲解決髒數據的問題。
對於分佈式系統的吞吐量有較高的要求,以及可以知足最終一致性的場景。如上面提到的告警機制及消費補償,分佈式事務是對微服務系統的完善,可是並不能徹底保證一致性,可能須要經過告警等手段解決極端問題產生的不一致狀況。
本文主要介紹了分佈式事務的相關概念以及業界一些經常使用的解決方案(參考了不少網上的博客),並提出了筆者基於可靠消息方案的實現:Lottor。後續文章將會詳細介紹Lottor的實現,敬請期待。