基於可靠消息方案的分佈式事務:Lottor介紹

前言:筆者最近實現了基於可靠消息方案的分佈式事務:Lottor。本文將會介紹Lottor的概況,在後續系列文章介紹具體的實現,歡迎關注。html

分佈式事務

分佈式事務是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位於不一樣的分佈式系統的不一樣節點之上。算法

首先,解釋下事務的概念:一組操做要麼都完成以後提交,要麼所有回滾。分佈式事務特指在分佈式環境下,一次事務設計多個服務進程,說白了就是跨進程的事務,這樣就不能控制事務組的一致性。數據庫

分佈式系統區別於傳統的單體應用,單體應用的服務模塊和數據都在一個服務中,使用Spring框架的事務管理器便可知足事務的屬性。而分佈式系統中,來自客戶端的一次請求每每涉及多個服務,事務的一致性問題由此產生。服務器

CAP理論

CAP定理是由加州大學伯克利分校Eric Brewer教授提出來的,他指出WEB服務沒法同時知足一下3個屬性:微信

  • 一致性(Consistency) : 客戶端知道一系列的操做都會同時發生(生效)
  • 可用性(Availability) : 每一個操做都必須以可預期的響應結束
  • 分區容錯性(Partition tolerance) : 即便出現單個組件沒法可用,操做依然能夠完成

具體地講在分佈式系統中,在任何數據庫設計中,一個Web應用至多隻能同時支持上面的兩個屬性。網絡

上面這句話的表述,不少人都用過,是的,這是一種誤解。注意CAP定律的完整表述:Any networked shared-data system can have at most two of the three desired properties.架構

CAP 定律的前提是 P,當 P 決定後纔有 CA 的抉擇。所以,簡單粗暴地說「三選二」是有必定誤導性的。併發

BASE理論

在分佈式系統中,咱們每每追求的是可用性,它的重要程序比一致性要高,那麼如何實現高可用性呢? 前人已經給咱們提出來了另一個理論,就是BASE理論,它是用來對CAP定理進行進一步擴充的。BASE理論指的是:框架

  • Basically Available(基本可用)
  • Soft state(軟狀態)
  • Eventually consistent(最終一致性)

BASE理論是對CAP中的一致性和可用性進行一個權衡的結果,理論的核心思想就是:咱們沒法作到強一致,但每一個應用均可以根據自身的業務特色,採用適當的方式來使系統達到最終一致性(Eventual consistency)。異步

需求分析

功能需求

功能需求最主要的是知足分佈式事務的一致性,涉及的事務組中的操做爲多個寫操做,當產生一個或多個寫操做失敗時,回滾整個事務組中的操做。

非功能需求

  • 性能:分佈式事務對系統的性能必然是有影響的,須要尋找平衡的點。
  • 高可用:引入中間件或者協調者時,避免單點故障。分佈式系統的高可用必然會犧牲部分一致性。
  • 可擴展:下降引入的業務耦合。
  • 伸縮性:系統可以彈性伸縮。

解決方案

強一致方案

X/Open 組織(即如今的 Open Group )定義了分佈式事務處理模型。 X/Open DTP 模型( 1994 )包括應用程序( AP )、事務管理器( TM )、資源管理器( RM )、通訊資源管理器( CRM )四部分。

XA 就是 X/Open DTP 定義的交易中間件與數據庫之間的接口規範(即接口函數),交易中間件用它來通知數據庫事務的開始、結束以及提交、回滾等。 XA 接口函數由數據庫廠商提供。

2PC

二階段提交的算法思路能夠歸納爲:參與者將操做成敗通知協調者,再由協調者根據全部參與者的反饋情報決定各參與者是否要提交操做仍是停止操做。第一階段:準備階段(投票階段)和第二階段:提交階段(執行階段)。

2pc
2pc

  • 同步阻塞問題。執行過程當中,全部參與節點都是事務阻塞型的。當參與者佔有公共資源時,其餘第三方節點訪問公共資源不得不處於阻塞狀態。
  • 數據不一致。在二階段提交的階段二中,當協調者向參與者發送commit請求以後,發生了局部網絡異常或者在發送commit請求過程當中協調者發生了故障,這回致使只有一部分參與者接受到了commit請求。而在這部分參與者接到commit請求以後就會執行commit操做。可是其餘部分未接到commit請求的機器則沒法執行事務提交。因而整個分佈式系統便出現了數據部一致性的現象。
  • 二階段沒法解決的問題:協調者在發出commit消息以後宕機,而惟一接收到這條消息的參與者同時也宕機了。那麼即便協調者經過選舉協議產生了新的協調者,這條事務的狀態也是不肯定的,沒人知道事務是否被已經提交。

3PC

三階段提交(Three-phase commit),也叫三階段提交協議(Three-phase commit protocol),是二階段提交(2PC)的改進版本。

3pc
3pc

若是由於協調者或網絡問題,致使參與者遲遲不能收到來自協調者的commit或rollback請求,那麼參與者將不會如兩階段提交中那樣陷入阻塞,而是等待超時後繼續commit。相對於兩階段提交雖然下降了同步阻塞,但仍然沒法避免數據的不一致性。在分佈式數據庫中,若是指望達到數據的強一致性,那麼服務基本沒有可用性可言,這也是爲何許多分佈式數據庫提供了跨庫事務,但也只是個擺設的緣由,在實際應用中咱們更多追求的是數據的弱一致性或最終一致性,爲了強一致性而丟棄可用性是不可取的。

柔性事務

根據BASE理論,系統並不保證續進程或者線程的訪問都會返回最新的更新過的值。系統在數據寫入成功以後,不承諾當即能夠讀到最新寫入的值,也不會具體的承諾多久以後能夠讀到。

弱一致性的特定形式。系統保證在沒有後續更新的前提下,系統最終返回上一次更新操做的值。在沒有故障發生的前提下,不一致窗口的時間主要受通訊延遲,系統負載和複製副本的個數影響。DNS 是一個典型的最終一致性系統。 在工程實踐上,爲了保障系統的可用性,互聯網系統大多將強一致性需求轉換成最終一致性的需求,並經過系統執行冪等性的保證,保證數據的最終一致性。但在電商等場景中,對於數據一致性的解決方法和常見的互聯網系統(如 MySQL 主從同步)又有必定區別。

補償機制:TCC

TCC 其實就是採用的補償機制,其核心思想是:針對每一個操做,都要註冊一個與其對應的確認和補償(撤銷)操做。它分爲三個階段:

  • Try 階段主要是對業務系統作檢測及資源預留
  • Confirm 階段主要是對業務系統作確認提交,Try階段執行成功並開始執行Confirm階段時,默認 Confirm階段是不會出錯的。
  • Cancel 階段主要是在業務執行錯誤,須要回滾的狀態下執行的業務取消,預留資源釋放。

TCC
TCC

TCC與2PC協議比較:

  • 位於業務服務層而非資源層
  • 沒有單獨的準備(Prepare)階段,Try操做兼備資源操做與準備能力
  • Try操做能夠靈活選擇業務資源的鎖定粒度(以業務定粒度)
  • 較高開發成本

本地消息表

相似於可靠消息方案。

本地消息表
本地消息表

消息生產方,須要額外建一個消息表,並記錄消息發送狀態。消息表和業務數據要在一個事務裏提交,也就是說他們要在一個數據庫裏面。而後消息會通過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的結構

Lottor由三部分組成:

  • Lottor Server
  • Lottor Client
  • Lottor UI

Lottor服務器與客戶端之間的通訊使用的高性能通訊框架:Netty。全部的客戶端(生產端和消費端)都會與服務器保持長鏈接。Lottor UI用於展現系統中的事務組詳細信息,包括預提交的事務組、消費失敗的事務消息,並支持頁面操做失敗的消息(如補償或重試)。

Lottor的設計

功能介紹

生產方分爲三步:

  • 預發送消息,首先會將消費方的事務組(一條或多條事務消息)組裝好,併發送到Lottor Server,事務消息的狀態爲預發送
  • 執行本地事務:預發送以後,將會執行本地事務。
  • 發送確認消息:根據本地事務的執行結果,異步發送確認消息。若是本地事務出現異常,回滾本地事務,並將異常信息捕捉一塊兒發送到Lottor Server。本地也會持久化該狀態(按期刪除)。

Lottor Server:

  • 接收預提交消息:收到預提交消息,將事務組中的事務消息分別保存,狀態爲pre-commit
  • 接收確認消息:狀態爲confirm,將更改相應的事務組狀態,並將消息發送到對應的消費方(MQ異步實現),並標記事務消息的狀態爲unconsumed。不然,回滾狀態只會修改事務組狀態(按期刪除)。
  • 回查預發送消息的狀態:狀態爲pre-commit的事務組消息,Lottor Server將會按期回查生產方。
  • 回查事務消息的狀態:狀態爲unconsumed(通常4h),Lottor Server將會按期回查消費方。

消費方:

  • 接收事務消息:訂閱相關的主題,消費完成以後,將會異步發送ACK給Lottor Server,消費失敗會將異常返回給Lottor Server。本地也會持久化消費的狀態(按期刪除)。

Lottor 客戶端的持久化,提供了SPI接口,可經過配置動態指定。目前支持:JDBC、Redis、MongoDB和文件系統。

告警機制及消費補償

這裏所說的告警機制及消費補償是針對消費端,可靠消息方案是保證了事務消息必定可以到達消費方,可是消費方可能由於某些緣由而沒法成功消費,有些消費異常是能夠經過重試解決的,而有些異常是須要告警以後人工干預的。好比消費方暫時不可用,或者是多個消費方消費的順序問題,能夠經過定時的重試機制完成。而若是是因爲生產方發送的事務消息出錯(參數構造錯誤),此時消費方已經提交了本地事務組,因此是沒法經過重試實現成功消費,致使須要告警,人爲解決髒數據的問題。

適用場景

對於分佈式系統的吞吐量有較高的要求,以及可以知足最終一致性的場景。如上面提到的告警機制及消費補償,分佈式事務是對微服務系統的完善,可是並不能徹底保證一致性,可能須要經過告警等手段解決極端問題產生的不一致狀況。

項目截圖

項目結構

UI界面

首頁

事務組信息

事務組狀態

總結

本文主要介紹了分佈式事務的相關概念以及業界一些經常使用的解決方案(參考了不少網上的博客),並提出了筆者基於可靠消息方案的實現:Lottor。後續文章將會詳細介紹Lottor的實現,敬請期待。

訂閱最新文章,歡迎關注個人公衆號

微信公衆號

參考

  1. 聊聊分佈式事務,再說說解決方案
  2. 分佈式事務 - 兩階段提交與三階段提交
  3. 關於分佈式事務、兩階段提交協議、三階提交協議
  4. 分佈式開放消息系統(RocketMQ)的原理與實踐
  5. TCC型分佈式事務原理和實現之:原理介紹
  6. 分佈式事務之說說TCC事務
相關文章
相關標籤/搜索