微服務架構中的分散式事務全面詳解 -DZone微服務

banq發表於2021-01-07

本文探討在微服務架構中實現事務處理時出現的挑戰以及用於處理它們的可能解決方案。

當從單體或整體架構遷移到微服務架構(MSA)時,處理分散式系統帶來的複雜性是一項挑戰。事務處理是此問題的重點。使用本地事務在Web應用程式中完成的典型資料庫事務現在是一個複雜的分散式事務問題。在本文中,我們將討論導致這種情況的原因,可能的解決方案以及使用MSA開發安全交易軟體系統的最佳實踐。

如果您已經熟悉了資料庫事務背後的相關概念以及分散式系統中資料一致性的複雜性,則可以跳到“微服務體系結構中的資料建模”部分,我們將探討如何在現實世界中使用資料建模案件。

 

資料庫事務:入門

在我們良好的舊式整體應用程式中,我們進行了資料庫事務以實現全有或全無的資料操作,同時保持資料的一致性。我們主要使用ACID事務,這是您在關聯式資料庫系統中找到的。這是快速複習: 

  • 原子性:所有操作都成功執行,或者所有操作一起失敗。
  • 一致性:資料庫中的資料按照其規則(例如參照完整性)規定保持有效狀態。
  • 隔離:併發執行的分離事務不能相互干擾。應該是因為事務在其自己的隔離環境中執行,在該環境中其他事務無法看到該持續時間內發生的更改。
  • 耐用性:提交事務後,更改將被持久儲存,例如持久儲存在磁碟中,因此資料庫伺服器的臨時崩潰不會丟失資料。 

我們可以想象使用ACID事務,如下所示,我們將一些資金從一個帳戶轉移到另一個帳戶。

的SQL

BEGIN TRANSACTION
  UPDATE ACCOUNT SET BALANCE=BALANCE-AMOUNT WHERE ID=1;
  UPDATE ACCOUNT SET BALANCE=BALANCE+AMOUNT WHERE ID=2;
END TRANSACTION

在這裡,我們將單個借記和貸記操作包裝在ACID事務中。這樣可以避免出現不一致的情況,例如,如果資金從一個帳戶中取出而沒有放入另一個帳戶中,則系統中會損失金錢。這是一個清晰,直接的解決方案,我們將在需要時繼續編寫此類程式碼。 

我們習慣了在需要時享受ACID事務的奢華。對於將處理需求儲存​​在單個資料庫伺服器中的大多數典型使用者,此模型很好。但是對於需要根據對資料訪問,儲存容量和讀/寫擴充套件的不斷增長的需求來擴充套件系統的人們而言,這種架構風格很快就會瓦解。對於這些使用者,有兩種擴充套件資料儲存的方式:

  • 垂直擴充套件:將功能更強大的硬體放在單個伺服器上,例如增加CPU和RAM或移至大型計算機。通常這比較昂貴,並且在無法再升級硬體時將達到極限。
  • 水平擴充套件:新增了更多的伺服器節點,並且所有內容都通過計算機網路進行了連線。這通常是最實際的操作。 

在水平擴充套件中,由於資料儲存在叢集中具有多個節點,因此事情變得有些複雜。由於資料駐留在物理上分開的伺服器中,因此出現了一系列新的挑戰。CAP定理對此進行了解釋,該定理說在分散式資料儲存中只能實現以下兩個屬性。

  • 一致性:這與節點之間的資料一致性有關。資料將具有副本,以在出現故障的情況下提供冗餘並提高讀取效能。這意味著當在單個位置完成資料更新時,應在所有其他副本中同時更新資料,而不會給客戶端造成任何延遲。這在形式上更具有線性化性。如您所見,這與ACID中的一致性概念不同。
  • 可用性:分散式資料儲存具有很高的可用性,因此伺服器例項的丟失不會妨礙整體資料儲存的功能,並且使用者仍將獲得對其請求的無錯誤響應。 
  • 分割槽容限:資料儲存區可以處理網路分割槽。網路分割槽是網路中某些節點之間的通訊丟失。因此,某些節點可能無法與資料儲存群集中的其他節點通訊。從資料庫叢集中的節點的角度來看,這有效地將資料儲存節點分割槽到多個本地網路。外部客戶端仍然可以訪問資料庫群集中的所有節點。 

因此,不可能同時具有一致性,可用性和分割槽容限。如果我們考慮可能發生的情況,我們可以直觀地理解這種行為。

為了在寫入資料時保持一致性,我們需要同時寫入所有充當副本的伺服器。但是,如果網路中有分割槽,我們將無法執行此操作,因為那時我們只能訪問某些伺服器。在這種情況下,如果我們想在保持一致性的同時容忍這些分割槽,就不能讓使用者從資料儲存中讀取不一致的資料。這意味著我們需要停止響應使用者請求,從而使資料儲存不再可用(一致且分割槽容忍)。另一種情況是讓資料儲存仍然起作用,即保持其可用狀態,這將使其不再一致(可用且具有分割槽容忍性)。 

最終的情況是系統不容許分割槽保持一致且可用。在這種情況下,為了具有強一致性(線性化),我們仍然必須使用事務協議(例如兩階段提交(2PC))在複製的資料庫伺服器節點之間執行資料操作。在2PC中,參與事務的資料庫的操作由事務協調器在以下兩個階段執行:

微服務架構中的分散式事務全面詳解 -DZone微服務

  • 準備:要求參與者檢查其資料操作是否可以執行,並向事務協調員提供保證,以後如果需要,可以提交操作或回滾。這是為所有參與者完成的。 
  • 提交:如果所有參與者在準備階段都表示可以,則為每個參與者提供一個提交操作,以執行前面提到的操作。在這一點上,由於較早地給予事務協調員的承諾,參與者無法拒絕該操作。如果任何參與者較早地由於任何原因拒絕了其本地資料操作,則座標將向所有參與者傳送回滾命令。 

除了上述用於可伸縮性的資料庫複製方案之外,在不同型別的系統(例如資料庫伺服器和訊息代理)之間執行事務時,還將使用2PC。但是,由於分散式參與者中鎖爭用的增加,我們通常避免使用2PC,這會導致效能下降並阻礙可伸縮性。 

實際上,計算機網路並不可靠,我們應該期望它們一次或一次具有網路分割槽。因此,我們通常會看到優先考慮可用性或一致性的資料庫系統,即AP或CP。一些系統允許使用者調整這些引數以使其高度可用或選擇一致性級別。資料庫系統(例如Amazon的DynamoDB和Apache Cassandra)中提供了此功能。但是,由於它們不具有嚴格的CAP級別一致性,因此通常被歸類為AP系統。Cassandra的輕量級事務支援使用Paxos共識協議來實現可線性化的一致性,但這很少使用,因為它的機制需要非常低的效能,這是可以預期的。

  

可伸縮性對資料一致性的影響

當我們檢查CAP定理的權衡時,如果我們重視高可用性以及高效能和可伸縮性,則必須在資料一致性方面做出折衷。CAP中的此資料一致性屬性會影響ACID的隔離屬性。如果分散式系統中節點之間的資料不一致,則意味著存在事務隔離問題,我們可以看到髒資料。因此,在這種情況下,我們必須接受現實並找到一種無需ACID事務和完全CAP一致性的生活方式。

擁抱最終一致性的這個事務的一致性模型也被稱為CAP定理,這促進了是可用最終一致性。軟狀態表示資料可能會由於最終的一致性而在以後更改。大多數NoSQL資料庫都遵循這種方法,在這種方法中,它們不提供任何ACID事務功能,而是專注於可伸縮性。 

在許多用例中,由於不需要嚴格的資料一致性,最終的一致性還可以。例如,域名系統(DNS)基於最終的一致性模型。多箇中間快取包含DNS條目。如果有人更新了DNS條目,則不會立即更新這些條目,而是在對本地條目執行快取超時後執行DNS查詢。由於不經常更新DNS條目,因此對每個名稱解析進行新的DNS查詢只是過大,並且將成為主要的網路效能瓶頸。因此,在DNS中為使用者提供過時的條目是可以容忍的。同樣,在許多其他現實世界中,我們也會這樣做。我們將實施其他變通辦法,以檢測這種型別的過時資料或不一致之處,並在此時採取適當的措施, 

在我們的分散式資料儲存方案中,一致性級別取決於我們正在實現的用例。讓我們更詳細地瞭解存在的各種一致性級別,從最高到最低的一致性級別。

  • 嚴格的可序列化

在嚴格的可序列化性中,應該在所有副本中自動進行多個物件操作,同時保持實時排序。實時排序意味著它與客戶端針對每個人都共享的全域性時鐘執行的操作相同。這些分組的物件操作代表單個事務。

這是實現ACID事務的隔離方面所必需的。因此,如果我們需要在ACID事務中發現典型行為以應對工作量,則在分散式資料儲存中需要嚴格的可序列化性。 

  • 線性化

在此一致性模型中,整個副本中單個物件的操作應該是原子的。當單個客戶端看到對副本中的物件執行的操作時,連線到任何其他副本的任何其他客戶端也應看到相同的操作。同樣,對物件執行操作的順序應與所有客戶端所看到的順序相同,並且類似於相同的實時順序。 

什麼時候需要?假設我們有三個客戶/程式。程式A將物件值寫入資料儲存。程式B收到一些外部事件,提示它讀取上述物件值。讀取該值後,它將向程式C傳送一條訊息,以讀取物件值並做出決定。對過程B的理解是,它讀取的物件值將至少是它具有的最新值,而不是舊值。為了確保這種行為,分散式資料儲存必須提供線性化一致性保證,以確保由程式A完成的更新可同時被所有其他副本立即看到。 

即使我們使用基於仲裁的讀/寫之類的方法將Cassandra資料庫配置為具有強一致性,也不會提供線性化保證,因為更新不是原子性地在副本中進行的。為了實現線性化,我們必須在Cassandra中使用輕量級事務支援。 

  • 順序一致性

在順序一致性方面,在資料儲存上執行操作的流程將以與其他流程相同的順序出現。同樣,所有操作的這種順序將與每個流程操作的相對順序一致。基本上,它按操作的整體順序保留了流程級別的順序。

  • 因果一致性

在因果一致性方面,所有潛在因果相關操作應以相同順序顯示在所有流程中。簡而言之,如果您基於先前觀察到的單獨操作進行操作,則這些操作的順序對於其他程式也必須相同。 

為了滿足因果一致性,應支援以下行為:

  • 讀取您的寫入內容:一個程式應該能夠立即讀取它在早期寫入操作中所做的更改。 
  • 單調讀取:對物件執行讀取操作的程式應始終看到相同或更新的值。基本上,讀取操作無法返回並看到較舊的值。 
  • 單調寫:執行寫操作的程式應確保其他程式應按其相對順序檢視這些寫操作。 
  • 寫跟隨讀取:程式通過基於較早的寫操作讀入較早的值v1,將新值v2寫入物件。在這種情況下,每個人都應該始終在v2之前看到物件的值v1。

這是在許多實際實際應用中特別有用的一致性模型。例如,讓我們來看看一個由分散式資料儲存支援的社交媒體網站。無論網站是更新其個人資料狀態還是評論某人的個人資料狀態,我們都有併發使用者與該網站進行互動。剛剛發生碰撞小事故的使用者Anne的狀態為“發生小事故,正在等待X-ray結果!”。處於此狀態後,她即可獲得結果,並且沒有骨折。因此,她將自己的狀態更新為“好訊息:沒有骨折!”。鮑勃(Bob)看到了來自安妮(Anne)的最後一條訊息,並回復她的狀態說:“很高興聽到!:)”。 

在上述情況下,如果我們的資料儲存至少提供因果一致性,則所有其他使用者將以正確的順序檢視來自Anne和Bob的訊息。但是,如果資料儲存不提供因果一致性,則其他使用者可能會看到Anne的第一條訊息,然後看到Bob的訊息,而看不到Anne的第二條更新。這種情況變得有些奇怪,因為鮑勃似乎對安妮的不幸表示高興,但事實並非如此。因此,在這種情況下,我們需要因果一致性。 

因此,假設在相同情況下我們有一個因果關係一致的資料儲存,那麼假設Tom更新他的狀態為“我剛買了第一輛車!” 就在安妮(Anne)第一次更新之前。網站的某些使用者在安妮的第一條訊息之後看到了他的狀態。這種情況很好,因為與Tom的更新發生在現實中的Anne更新之前還是之後無關緊要。它們之間沒有聯絡,即湯姆的行為不是由安妮的行為引起的。沒有因果關係的其他操作以最終一致的方式操作。 

MongoDB是專門支援因果一致性的分散式資料儲存。它基於Lamport邏輯時鐘實現了這一點。 

  • 最終一致性

在最終的統一性中,如果不再對資料儲存進行寫操作,則資料儲存中的所有副本都將收斂並最終商定最終值。它不會提供任何其他保證,例如因果一致性,直到它穩定在最終值上。 

實際上,這種一致性模型適用於用例,這些用例只是併發值更新,而值在更新時之間沒有任何聯絡。使用者並不關心中間值,而只關心最終的最終穩定值。例如,讓我們建立一個釋出每個城市當前溫度的網站。這些值會不時變化。在某些時候,某些使用者可能會看到最新的溫度值,而其他使用者仍未更新。但是最終,資料更新將被網站的所有使用者追上。因此,只要最終所有使用者最終都將看到相同的溫度值,儲存這些值的分散式資料庫的傳播延遲就不會成為大問題。

有關事務一致性模型的更多詳細資訊,請檢查本文結尾的資源部分。

現在,我們對與事務處理和一致性模型有關的方面有了一般的瞭解。當您在任何分散式處理環境(例如MSA)中工作時,此知識很有用。我們所討論的相同概念也將適用於此。現在讓我們看看如何在MSA中對資料建模。 

 

微服務架構中的資料建模

微服務的基本要求是高度凝聚力和鬆散耦合。這自然是必需的,因為開發團隊的組織結構也將圍繞此概念構建。將有單獨的團隊負責微服務,他們需要靈活性和自由才能獨立於其他人。這意味著他們避免在設計和實現的內部細節上與其他團隊進行不必要的同步。 

根據這些要求,微服務應嚴格禁止共享資料庫。如果每個微服務都不能擁有自己的資料庫,那麼這很好地表明瞭這些微服務需要合併。 

下面顯示了電子商務後端可能的微服務設計。 

微服務架構中的分散式事務全面詳解 -DZone微服務

在這裡,我們使用自己的微服務來管理系統的各個方面。在我們開始處理事務之前,這看起來不錯。典型的操作將包括使用一組產品建立使用者訂單。使用庫存服務檢查這些產品的可用性,並在完成訂單後更新庫存以減少那些產品的可用庫存。在典型的整體應用程式中,您可以在單個ACID事務中執行以下操作。 

的SQL

BEGIN TRANSACTION

  CHECK INVENTORY OF PRODUCT 1 FOR 5 ITEMS
  CHECK INVENTORY OF PRODUCT 2 FOR 10 ITEMS
  CREATE ORDER
  ADD 5 ITEMS OF PRODUCT 1 TO ORDER
  ADD 10 ITEMS OF PRODUCT 2 TO ORDER
  DECREMENT INVENTORY OF PRODUCT 1 BY 5 ITEMS
  DECREMENT INVENTORY OF PRODUCT 2 BY 10 ITEMS
  INSERT PAYMENT 
END TRANSACTION

通過這種方法,我們相信資料儲存在資料操作之後最終會保持一致狀態。現在,我們如何使用微服務對上述操作建模?可能想到的一種解決方案是以下方法。 

微服務架構中的分散式事務全面詳解 -DZone微服務

在這裡,協調器服務“ Admin”通過呼叫每個服務的操作來建立服務編排。如果所有操作執行沒有任何問題,這將起作用。但是,流程中的某些步驟很有可能會失敗,例如,當使用者的信用額度不足或網路通訊失敗時,如果發生應用程式錯誤。例如,如果由於使用者管理服務對於支付處理服務不可用而導致流程失敗,則步驟4將失敗。但是目前,我們已經建立了一個訂單並更新了庫存。因此,現在我們的系統處於不一致狀態,在該狀態下,庫存報告的商品數量減少了,而沒有人購買!此處的明顯問題是,我們沒有在單個事務中執行這些操作,如果一步失敗, 

解決我們的問題有哪些可能的解決方案?最簡單的解決方案將是通過將所有這些操作放到單個服務和單個資料庫中,並在本地事務中完成所有操作來返回整體解決方案。但是在這種情況下,我們假設已經做出了大型單片應用程式無法擴充套件的決定,並且必須將其分解為單個微服務。

在這種情況下,對於我們的事務問題,我們只剩下基於2PC的解決方案。我們可以使用WS-TXBallerina分散式事務解決方案在網路服務之間執行基於2PC的全域性事務的功能。如果您需要在事務中使用ACID擔保,則只有類似的方法才是選擇。但是,應謹慎使用,因為典型的2PC缺點(如增加後端資料庫中的鎖定時間)仍然存在。由於額外的網路通訊躍點,這種性質僅在微服務環境中有所增加。 

但是,大多數現實工作流程不需要ACID保證,因為可以使用與操作相反的操作來逆轉錯誤的操作。因此,在訂單處理工作流程中,如果出現問題,我們可以為已完成的操作執行補償操作並回滾整個事務。這些操作將包括諸如將付款退回到使用者的信用卡,通過增加訂單中產品的數量來更新產品庫存以及更新已取消的訂單記錄。 

實際上,我們的電子商務後端方案不能完全建模為單個資料庫事務,因為處理付款的操作是使用外部付款閘道器完成的,該閘道器不能是本地或全域性(2PC)資料庫事務的一部分。但是,在基於2PC的全域性事務中,這種情況是一個例外。在2PC場景中,全域性事務的最後一個參與者不必同時執行準備階段和提交階段,但是單個準備操作就足以執行其操作。這稱為最後一次資源提交優化。同樣,這僅在這種特定情況下才有可能,在這種特定情況下,這種型別的參與者在工作流中任何其他位置都將無法進行全域性事務。 

因此,現在我們決定不再需要使用2PC獲得的嚴格資料一致性,並且以後可以更正所有問題。讓我們看一下該工作流程的可能執行方式。 

微服務架構中的分散式事務全面詳解 -DZone微服務

在這裡,工作流在步驟4失敗。從那以後,管理服務應以與先前呼叫的服務相反的順序執行一組補償操作。但是這裡有一個潛在的問題。如果管理服務在還原操作時遇到錯誤(例如臨時網路問題)怎麼辦?我們再次遇到了資料不一致的問題,其中未完成總回滾,也無法知道我們上次執行的操作以及以後如何解決。 

處理此問題的一種方法是保留由admin服務完成的操作的日誌。這可能類似於以下內容:

  • TX1:檢查庫存
  • TX1:建立訂單
  • TX1:更新庫存
  • TX1:流程付款-失敗
  • TX1:取消標記訂單
  • TX1:更新庫存-庫存增加計數

因此,管理服務可以跟蹤已執行的操作以及未執行的操作。但是同樣,我們必須考慮即使在處理這樣的事件日誌時也可能發生的極端情況。管理服務及其日誌與其他遠端服務操作是分開的,因此,這些互動本身無法進行事務處理。有如下更改:

  • 管理員服務執行庫存服務以還原庫存計數(通過遞增)
  • 管理員服務使用“ TX1:更新庫存-庫存增加計數”更新其日誌

如果執行上面的第一個操作,然後服務在第二個操作之前崩潰,該怎麼辦?當admin服務再次繼續其操作時,它將認為它沒有執行庫存恢復操作,並將再次執行第一個操作。這將使庫存資料具有無效值,因為它使庫存數增加了兩次,這不好!正如我們在分散式系統中經常看到的那樣,這是一次交付最少的情況。解決此問題的常用方法是將我們的操作建模為冪等的。也就是說,即使多次執行相同的操作,也不會造成任何損害,並且目標系統的狀態將相同。 

但是我們的庫存回滾操作不是冪等的,因為它沒有設定特定的值,而是在目標系統中遞增已經存在的值。因此,您無法複製這些操作。通過直接設定訂單之前的存貨盤點,我們可以使其成為冪等操作。但是再說一次,由於我們的事務在微服務體系結構中建模的方式,它沒有提供您在ACID事務中會發現的任何型別的隔離屬性,即嚴格的序列化一致性級別。也就是說,當執行我們的事務時,另一個使用者也可能正在建立另一個涉及相同產品的訂單,這將修改相同的庫存記錄。因此,這兩項事務的操作可能會重疊並導致不一致的情況。 

因此,實際上,不僅在事務回滾的情況下,即使在兩個併發成功的事務中,由於缺乏隔離,冪等操作也可能導致資料丟失更新。讓我們檢查下面兩個事務操作的時間表。 

微服務架構中的分散式事務全面詳解 -DZone微服務

在這裡,我們有兩個事務TX1和TX2。他們倆都在為產品“ P1”下訂單,該產品的初始庫存為100。TX1將建立一個包含10個專案的訂單,然後TX2將建立一個包含20個專案的訂單。從上面的序列圖中可以看出,檢查庫存和更新庫存不是在兩個事務中原子發生的,而是它們相互交錯,最終TX2的庫存更新使TX1的庫存更新蒙上了陰影。因此,最終P1的產品數量為80,而由於兩次交易事務購買了30種產品,應該為70。因此,現在庫存資料庫資料錯誤地發出了實際可用庫存的訊號。

由於兩個併發程式未正確隔離,因此我們出現了競爭狀況。解決這種情況的方法是,使庫存更新操作相對於資料庫中的現有值進行值遞減操作,即decrementInventoryStockCount(product,offset)。因此,可以使用單個SQL操作或正在執行的單個本地事務將該操作作為目標服務中的原子操作。 

因此,通過此更新,可以按照以下方式重寫上面的互動。 

微服務架構中的分散式事務全面詳解 -DZone微服務

正如我們現在所看到的,隨著新操作減少庫存中的盤點,我們的最終盤點將保持一致和正確。 

注意:我們仍然會有不同的錯誤情況,在檢查初始庫存計數後,在結帳時,其他事務已經帶來了庫存的產品庫存可能是空的。這只是作為業務流程錯誤處理,我們可以回滾操作,並且資料仍然是一致的。 

有時由於我們在微服務通訊中遇到的事務隔離問題,使資料操作成為冪等是不可行的。這意味著,如果不確定某個操作是否在遠端微服務中執行,就不能盲目地重試事務中的操作。這可以通過具有唯一識別符號(例如微服務呼叫操作的事務ID)來解決,因此目標微服務將建立針對該微服務執行的事務歷史。以這種方式,對於每個微服務操作呼叫,它可以執行本地事務以檢查歷史記錄,並檢視該事務是否已被執行。如果不是,它將執行資料庫操作,並且仍在本地事務中,同時也更新事務歷史記錄表。以下程式碼顯示了可能的實現使用以上策略在庫存服務中執行decrementInventoryStockCount操作。 

function decrementInventoryStockCount(txid, pid, offset) {
   transaction {
       tx_executed = check transaction table record where id=txid
       if not tx_executed {
           prod_count = select count from inventory where product=pid
           prod_count += offset
           set count=prod_count in inventory where product=pid
           insert to transaction table with id=txid
       }
   }
}

 

微服務和訊息傳遞

因此,現在,我們已經找到了一種一致的方式來執行一組微服務中的事務,並最終實現了全有或全無的保證。在此流程中,我們仍然必須維護事件日誌並在可靠的永續性儲存中對其進行更新。如果執行業務流程的協調服務發生故障,則另一個實體將不得不再次觸發它以檢查事件日誌並執行任何恢復操作。如果我們已經有了一些可用於在服務之間提供可靠通訊以幫助完成此任務的中介軟體,那將是很好的。 

這是事件驅動架構(EDA)有用的地方。這可以使用訊息代理在微服務之間進行通訊來建立。使用這種模式,我們可以確保是否成功將訊息傳送給旨在針對某些服務的訊息代理,該訊息將在某個時候成功傳送給目標收件人。這種保證使我們的其他流程更容易建模。此外,由於訊息代理的非同步通訊模型可以同時進行讀取和寫入操作,因此其開銷要小一些,並且可以減少往返呼叫的等待時間,因此可以提供更好的效能。錯誤處理也更加簡單,因為即使目標服務關閉,訊息代理也會保留訊息並在目標端點可用時將其傳遞。另外,它可以執行其他操作,例如重試故障和使用多個服務例項進行負載平衡請求。這種模式還鼓勵服務之間的鬆散耦合。通訊通過佇列/主題進行,並且生產者和消費者不需要顯式地彼此瞭解。的在基於補償操作的微服務中實現事務時,Saga模式遵循這些通用準則。 

有兩種實現該模式的協調策略:編排和編排。 

  • 編舞

在這種方法中,服務本身知道操作流程。在將初始訊息傳送到服務操作後,它將生成下一條訊息,該訊息將傳送到後續服務操作。服務需要對事務流有明確的瞭解,從而導致服務之間的更多耦合。下圖顯示了將事務工作流實現為編排時服務與佇列之間的典型互動。 

微服務架構中的分散式事務全面詳解 -DZone微服務

在這裡,我們可以看到流程從客戶端開始,該客戶端通過其輸入訊息佇列將初始訊息傳送到第一個服務。按照其業務邏輯,它可以在服務內定義的本地事務中執行與資料相關的操作。完成操作後,整個工作流程會將訊息新增到編排中下一個服務的請求佇列中。以這種方式,整個事務上下文將通過這些訊息傳播到每個服務,直到完成。 

如果工作流中的服務失敗,我們需要回滾整個事務。為此,從發生故障的服務開始,它將清理其資源,並通過補償佇列將訊息傳送到恰好之前執行過的服務。這將移動上一步的執行,執行任何補償操作以回滾其本地事務所做的更改,並重復聯絡其先前服務以進行補償操作的操作。這樣,錯誤處理鏈將到達第一個服務,然後該服務最終將訊息傳送到響應佇列。它已連線到客戶端,以通知發生了錯誤,並且已使用補償操作成功回滾了總事務。 

從同步服務呼叫方法可以看出,在各自服務中執行本地事務時,我們應維護事務歷史記錄表,以確保在服務接收重複訊息的情況下不重複本地操作。同樣,為了不丟失工作流的連續性,服務僅應在完成資料庫事務並將下一條訊息新增到後續服務的請求佇列之後,才從其請求佇列中確認該訊息。此流程確保我們不會丟失任何訊息,並且通過成功執行或回滾所有操作來完成整個事務的執行。 

  • 編排

在這種協調方法中,我們擁有專用的協調器服務,該服務連續呼叫其他服務。協調器服務與其他服務之間的通訊將通過請求和響應佇列進行。這種模式類似於我們電子商務場景中的“管理”服務。唯一的變化是使用訊息傳遞進行通訊。 

協調器服務與其他服務之間的非同步通訊使它可以將事務處理建模為狀態機,其中,用服務完成的每個步驟都可以更新狀態機。狀態機應保留在資料庫中,以從協調服務的任何故障中恢復。下圖顯示瞭如何使用訊息驅動策略設計業務流程協調方法。 

微服務架構中的分散式事務全面詳解 -DZone微服務

與編排相比,編排在服務之間的耦合較小。這是因為工作流由協調服務驅動,並且在給定時刻的完整狀態保留在該服務本身中。但是,這裡的服務並不是完全獨立的,因為它們的請求和響應都繫結到特定的佇列,固定的生產者和固定的消費者正在使用這些佇列。因此,現在將這些服務用作通用服務變得更加困難。 

當操作次數較少時,基於編排的協調可能是可行的。對於複雜的操作,在對操作進行建模時,基於業務流程的方法將具有更大的靈活性。

實施此策略時,重要的是在開發人員框架中抽象出更詳細的通訊細節,狀態機的永續性等。否則,開發人員最終將編寫更多程式碼來實現事務處理,而不是核心業務邏輯。而且,如果典型的開發人員總是從頭開始實施此模式,則容易出錯。

 

選擇微服務的事務模型

在實現事務時使用的任何技術中,我們都需要明確說明每種方法所能提供的資料一致性。然後,我們必須交叉檢查我們的業務需求,以瞭解最適合我們的業務。以下可以用作一般準則。 

  • 2PC:在使用不同的程式語言/框架/資料庫以及可能來自不同公司的開發團隊建立微服務的情況下,不可能將所有操作組合到一個服務中。而且,這將要求嚴格的資料一致性,在這種情況下,不能容忍與業務需求相關的任何資料隔離問題,例如髒讀。  
  • 基於補償的事務:這是使用事務協調機制來跟蹤事務中的各個步驟,並在失敗的情況下執行補償操作以回滾操作。這通常應涉及使用冪等資料操作或可交換更新,以便處理訊息重複的情況。您的業​​務需求應該能夠處理最終的一致性行為,例如髒讀。 
  • 合併服務:如果由於效能問題和可伸縮性而不能容忍2PC,請使用此功能,但是對於業務需求需要嚴格的資料一致性。在這種情況下,我們應該將相關功能分組到自己的單個服務中,並使用本地事務。

 

概要

在本文中,我們研究了從ACID保證到使用BASE放鬆資料一致性保證的事務處理基礎知識,以及CAP定理如何定義分散式系統中的資料儲存權衡。然後,我們分析了分散式資料儲存和一般分散式過程中可以支援的不同資料一致性級別。這些資料一致性問題直接適用於在MSA中對事務建模,在此我們需要將各個獨立的服務整合在一起以進行全域性事務。 

在選擇與業務需求相關的選項時,我們研究了每種方法的優點和缺點,並檢查了一般準則。 

 

相關文章