如何解決微服務事務難題

如何解決微服務事務難題

微服務架構下的應用是由一組松耦合的相互協調的服務所組成。這些服務內部通常使用獨立的數據庫來維護狀態,服務與服務之間是通過輕量級的通訊協議進行交互的。如何協調這些服務之間的分佈式事務一致性成為微服務架構需要解決的一個重要問題。

本文根據 QCon 姜寧演講稿整理而成,他結合業界普遍採用的 Saga 技術,以及 ServiceComb Saga 項目,與大家分享 Saga 分佈式事務最終一致性解決方案以及相關實踐經驗。

我先介紹一下我自己,我叫姜寧,來自於華為開源研究中心,現在負責的是 ServiceComb 這個開源項目。ServiceComb 這個項目已經進到 Apache 孵化,應該是去年 11 月份時進到 Apache 孵化的,這個月我們幫剛發了 1.0M1 版,但對於 SAGA 來說我們屬於探索的階段,發佈了 0.1.0。

我參與過 Apache 一些項目,我也是 Apache 的 Member,是 Apache CXF, Apache Camel 還有的 PMC。還有一件事情,前一段時間幫阿里同學孵化 RocketMQ,我是以導師的身份參與的,屬於見習的 Mentor。這回帶著 ServiceComb 這個項目,我感覺我把很多角色都玩了一遍。後面如果大家對開源項目進入 Apache 孵化感興趣的話也可以來找到。

今天的議題圍繞幾個方面來展開,一個是微服務事務一致性的問題,然後講一講業界的 SAGA 解決方案,我們在 ServiceComb 裡也提供了 SAGA 的實現。另外一件事情,我項目這邊也在招新,歡迎廣大感興趣的同學一會兒加我微信。我們現在項目其實架子也都搭得不錯了,就是等著大家跳到碗裡面,一起來解決微服務事務一致性問題。

微服務架構強調的服務能夠獨立開發,獨立演進,獨立部署, 獨立團隊。

那微服務相關的數據是採用什麼方式進行存儲的呢?

傳統的單體應用一般採用的是數據庫提供的事務一致性,通過數據庫提供的提交以及回滾機制來保證相關操作的 ACID,這些操作要麼同時成功,要麼同時失敗。各個服務看到數據庫中的數據是一致的,同時數據庫的操作也是相互隔離的,最後數據也是在數據庫中持久存儲的。這樣的架構不具備橫向擴展能力,服務之間的耦合程度也比較高,會存在單點故障。

在微服務架構中, 有一個 database per service 的模式, 這個模式就是每一個服務一個數據庫。 這樣可以保證微服務獨立開發,獨立演進,獨立部署, 獨立團隊。

由於一個應用是由一組相互協作的微服務所組成,在分佈式環境下由於各個服務訪問的數據是相互分離的, 服務之間不能靠數據庫來保證事務一致性。 這就需要在應用層面提供一個協調機制,來保證一組事務執行要麼成功,要麼失敗。

兩階段提交其實比較簡單,這邊有兩個資源提供準備和提交兩個接口。

如何解決微服務事務難題

由於隔離性互斥的要求,在事務執行過程中,所有的資源都是被鎖定的,這種情況只適合執行時間確定的短事務。 但是為了保證分佈式事務的一致性,大都是採用串行化的隔離級別來保證事務一致性,這樣會降低系統的吞吐。

但因為 2PC 的協議成本比較高,又有全局鎖的問題,性能會比較差。 因此現在大家基本上不會採用這種強一致解決方案。我們需要做一下酸鹼平衡。

這裡先簡單介紹一下酸鹼平衡中的酸 ACID。 原子性 事務作為整體來執行,要麼全部執行,要麼都不執行。一致性 事務應確保數據從一個一致的狀態轉變為另一個一致的狀態。隔離性 多個事務併發執行時,一個事務的執行不應影響其他事務的執行。持久性 已提交的事務修改數據會被持久保持。

如何解決微服務事務難題

酸鹼平衡中的鹼 BASE。 基本可用 可以保證分佈式事務參與方不一定同時在線。柔性狀態 允許系統狀態更新有一定的延時,這個延時對客戶來說不一定能察覺。最終一致性 通常是通過消息可達的方式保證系統的最終一致性。

前面講的分佈式事務的一些基礎理論,數據庫以及分佈式的兩階段提交都提供了 ACID 的保證。 由於隔離性互斥的要求,在事務執行過程中,所有的資源都是被鎖定的,這種情況只適合執行時間確定的短事務。後續大家開始通過業務邏輯將互斥鎖操作從資源層面上移到業務層面,這並不是完全放棄了 ACID,而是通過放寬一致性要求,藉助本地事務來實現最終分佈式事務一致性的同時也保證系統的吞吐。

TCC 名字的由來是其中包含了 try, confirm, cancel 三個操作。

如何解決微服務事務難題

與兩階段提交相比,TCC 位於業務服務層, 沒有單獨的準備階段,Try 操作可以靈活選擇業務資源鎖的粒度。TCC 是通過最終一致性來解決系統性能問題的這個設計,對我們設計抉擇有很大的啟發。 有些時候 系統的技術問題是可以通過業務建模的方式來解決的

回顧之前的酸鹼平衡的示例,我們得到的啟發我們可以通過業務模型的改進提升系統性能。有關領域建模,這裡給大家推薦兩本書,一個是《領域驅動設計》,還有一個是《實現領域驅動設計》。

微服務設計目標高內聚低耦合,領域驅動設計能幫助構建一致的業務模型和系統實現模型,通過領域驅動設計可以明確微服務的界限上下文。通過在業務層面上把它們之間的強耦合關係拆開之後,帶來最大的好處是,它們自身可以藉助傳統的數據庫所提供的功能來實現一致性。只不過是在微服務與微服務之間,它們需要通過前面提到的這種柔性事務方式來做這件事情。

微服務架構是一個在限定界限上下文內的松耦合的服務架構。微服務事務一致性的建議是什麼呢?就是內剛外柔。在限定上下文內容範圍內可以藉助數據庫提供事務一致性來做強一致。在限定上下文之間依靠最終一致性方案來解決服務間協同問題。

如何解決微服務事務難題

對於柔性事務來常見的實現方式有 TCC,和 Saga,今天我們主要向大家介紹 Saga 的實現。

Saga 其實是 30 年前的一篇數據庫論文裡提到的一個概念。在論文中一個 Saga 事務是由多個本地事務所組成, 每個本地事務有相應的執行模塊和補償模塊,當 saga 事務中的任意一個本地事務出錯了, 可以通過調用相關的補充方法恢復之前的事務,達到事務的最終一致性。

Saga 概念雖然提出來快 30 年了, 隨著微服務的普及,分佈式 Saga 問題近些年也逐步受到大家的關注。

我們在實現 Saga 模型的時候,主要是參考了 Caitie McCaffrey 在分佈式 Saga 論文,以及 Chris Richardson 的研究。 大家可以通過下面的鏈接獲取相關的信息。

在分佈式系統中由於網絡請求可能的延時,在 Caitie 的論文中對被 Saga 調用的服務提出兩點要求,我們需要 Saga 調用的服務支持冪等。 在服務請求的過程中,可能會出現超時重試的情況,我們需要通過冪等來避免對同一服務多次請求所帶來的問題。

如何解決微服務事務難題

前面提到了超時重試的機制,現在我們再來看看重試取消的情況。補償可交換原則是指 Saga 並行處理的過程中,如果發生了超時重試事件之後,並進行了補償的操作,那麼補償操作是直接生效的。

為了滿足這個要求,需要我們在設計系統的過程中保留所有的事務數據。

如何解決微服務事務難題

由此我們可以知道 Saga 模型只支持 ACD,不提供隔離性的保證。

如何解決微服務事務難題

因為 saga 事務沒有準備階段,事務沒有隔離,如果兩個 saga 事務同時操作同一資源就會遇到我們操作多線程臨界資源的的情況,會產生更新丟失,髒數據讀取等問題。

為了解決隔離性帶來的問題,我們可以參考一下 TCC 的解決方案,從業務層面入手加入一 Session 以及鎖的機制來保證能夠串行化操作資源。也可以在業務層面通過預先凍結資金的方式隔離這部分資源, 最後在業務操作的過程中可以通過及時讀取當前狀態的方式獲取到最新的更新。

目前業界提供了兩類 Saga 的實現方式。 一個是集中式協調器的實現方式,一個分佈式的實現方式。

集中式的 Saga 實現一般是通過一個 Saga 對象來追蹤所有的 Saga 子任務的調用情況, 根據調用情況來決定是否需要調用對應的補償方面,協調器和調用方是在一個進程中的。

如何解決微服務事務難題

如何解決微服務事務難題

這裡附上的是 Camel 最近實現的 Saga EIP, 通過這個 DSL 可以很方便地實現執行操作和取消操作。

集中式的 Saga 實現方式比較直觀並且容易控制,單其最大的問題是業務耦合程度會比較高,這樣為我們構建通用的 Saga 解決方案帶來了很大難度。

如何解決微服務事務難題

布式的 Saga 一般是採用事件驅動方式讓參與的服務方進行相關的交互。相關的業務方只需要訂閱相關的領域事件即可。 Chirs 提供了基於事件溯源的實現,同時 axonframework 也提供了相關的實現,詳細的內容大家可以參考上面的鏈接。

分佈式 saga 實現的好處: 採用事件源的方式降低系統複雜程度,提升系統擴展性, 處理模塊通過訂閱事件的方式降低系統的耦合程度。當然這也的實現也有一些問題: saga 系統會涉及大量的業務事件,這樣會對編碼和調試會帶來一些問題;還有就是相關的業務邏輯處理是基於事件,相關事件處理模塊可能會有循環依賴的問題。

前面理論鋪墊已經做完了,現在講一講我們的實現。 ServiceComb 是去年 5 月份華為開源的微服務框架,它是華為雲微服務框架引擎很重要的一個組成部分。大家可以通過下面鏈接來訪問 ServiceComb 官網以及相關的代碼庫。

目前 ServiceComb 主要有三個項目組成:第一個是 ServiceCenter 做服務發現的,這是 go 語言在 etcd基礎實現的服務註冊中心,另外是 java 的一個微服務框架,這塊是基於 vertx 的基礎上實現了全異步操作接口,框架有比較好的性能。 Saga 項目其實就是我們針對前面剛剛提到 Saga 模式的提供的一個實現。

如何解決微服務事務難題

集中式的 Saga 協調器構建過程參考了 Caitie 的論文,實現了一個集中式 Saga 調用協調器。 後續我們在開發的過程中,發現如果不提供 DSL 的話,讓用戶定義 Saga 事件很困難。後來我們受到 Zipkin 的啟發,我們實現了一個分佈式的 Saga 協調器。

集中式的協調器,包含了 Saga 調用請求接收,分析,以及執行和結果查詢這部分的內容。任務代理模塊需要預先知道 Saga 事務調用關係圖。執行模塊根據生成的調用圖產生調用任務,調用相關微服務服務接口。如果服務調用執行出錯,會調用服務的相關的補償方法回滾。

下面是我們實現的兩個不同的調度模型。

如何解決微服務事務難題

這裡 Saga 執行模塊通過分析請求的 Json 數據,構建一個調用關係圖。這裡我們是通過 JSon 來描述 Saga 事務串行調用子事務或者並行調用子事務。關係調用圖被 Saga 實現中的任務運行模塊分解成為一個一個執行任務,執行任務由任務消費者獲取並生成相關的調用 (這裡同時支持串行和並行調用)。

Saga 任務會根據執行的情況向 Saga log 中記錄對應的 Saga 事務的關鍵事件,同時我們的事件查看器查看到 Saga 事務相關的執行情況。

如何解決微服務事務難題

後續我們採用了 Actor 模型對任務的調度模塊進行了重構,在不進行調優的情況下,系統性能提升一倍。

集中式 Saga 的實現的好處是易於監控和協調, 但是壞處就是需要依賴工具對 Saga 調用進行相關的描述。那我們有沒有一個好的方式自動獲取 Saga 事務的定義呢? 為了解決這一問題,我們對內部的 Saga 實現進行新的改進。

正如前面提到的,Zipkin 通過在服務調用雙方傳遞上下文的方式可以構建出完整的調用鏈, 這個調用鏈和我們需要的 Saga 子事務調用信息很類似。 因此我們藉助 Zipkin 實現了一套 Saga 調用關係追蹤的模塊。

為了實現這個事務追蹤模型,就需要我們在應用端部署相關的監控模塊,同時監控模塊需要和後臺進行協同交互,於是我們設計了 Pack 狼群架構。

狼群架構有兩個參與者, 一個是 Alpha, 一個是 Omega。Alpha 是狼群首領,負責協調事務執行情況,Omega 是狼群成員,負責收集事務,向狼群首領上報情況,並執行相關指令。

Omega 會以切面編程的方式嚮應用程序注入相關的處理模塊。這裡有攔截請求的模塊, 用來幫助我們構建分佈式事務調用的上下文。 同時在事務處理初始階段處理事務的相關準備的操作,例如創建 Saga 起始事件,以及相關的子起始事件, 根據事務的執行的成功或者失敗生產相關的事務終止或者失敗事件。

Omega 會與 Alpha 進行鏈接會把這些事件通知給 Alpha。 Alpha 可以在後臺進行分析,根據 Saga 事務執行的情況給 Omega 下達相關的指令進行相關的回滾恢復。

這樣設計的好處是 Saga 實現代碼與用戶的代碼分離, 用戶只需要添加幾個 annotation,Saga 實現就能 Saga 事件的執行情況並進行相關的處理。

這是 Omega 和 Alpha 之間的正常業務邏輯的交互圖,這裡 Omega 通過分析調用上下文決定是否發生 Saga 事務的起始事件到 Alpha,後續 ServiceA 在調用 ServiceB,會將相關的調用上下文傳遞給 ServiceB。ServiceB 的 Omega 模塊會截取這個調用上下文生成相關子事務事件信息。

如果服務調用的過程中拋出異常,Omega 會將終止事件發送到 Alpha 端, Alpha 的後臺進程會定時做掃描,掃描過程中會發現有需要恢復的事件。Alpha 會向 Omega 發消息調用相關的恢復操作,來保證整個 Saga 事務的原子性。目前 Omega 也開始提供重試功能,也就是事務調用如果失敗了, Omega 會根據設置進行重試嘗試。

未來的開發計劃,第一個就是 Alpha 這邊會提供多租服務架構,這樣可以在雲上提供 Saga 協調服務;為了保證系統的可達性,我們可能會用消息隊列的方式來做傳遞事件信息。還有就是剛剛給大家提到的 TCC 協調器也是我們下一步的工作重點。如果能在 Omega 端來保證冪等操作的話,可以極大減輕大家的業務編程負擔。另外根據 Saga 的執行這個情況,也要做一些可視化的事情,這塊其實也是前端,大家如果有做前端技能的話,可以跟我聯繫一下。

還有 Omega 需要解決多線程共享調用的問題。


正如我們之前所分享的—— ,開源項目的魅力在於開放,Saga作為一個處在不斷的向前發展的過程中的項目,歡迎各路大神參與進來,共同貢獻,攻克微服務難題。大家可以通過訪問:http://t.cn/Rr7ccrc 查看認領Saga的所有JIRA任務,同時,也可以點擊下方瞭解更多進入我們的Github。

https://github.com/apache/incubator-servicecomb-saga。


分享到:


相關文章: