不知道大家有沒有想過,這麼多層的微服務當中,資料都是往一個或多個的在傳,我們該怎麼確保資料的一致性?
這會是一個相當複雜的問題,這篇我會分別介紹三種模式,並引用 ACID 與 CAP,對這方面不夠了解的朋友可以先了解一下喔!
接下來我要分別介紹 Two-Phase Commit Pattern、Saga Pattern 與 Eventual Consistency Pattern。這三個在分散式服務中較常見的模式,來看看哪一種最能在微服務中確保資料傳遞時的一致性。
Two-Phase Commit Pattern
Transaction Manager 會處理分散式交易,在準備交易時,它會跟相應的微服務做溝通,叫它們做好要交易的準備,這是 Prepare Phase。然後會進入 Voting Phase,這時 Transaction Manager 會對服務提出決議,看看現在是否適合 commit,若所有相應的服務都投出 Yes,則會開始進行 commit 指令。但如果有其中一個服務投出 No,這時 Transaction Manager 會因為有某組服務的失敗,如斷線、沒有資源、無法寫入等,提出 Rollback 指令,讓所有資料變回未寫入前。
聽之下,Two-Phase Commit Pattern 的做法好像滿好的,能有效保證資料一致性,但是。。。( 嗯,一定要說這個但是的,不然文章寫不下去呀 ),我們會遇到一點問題,如下:
- 過份依賴 Transaction Manager
如果今天掛掉的是 Transaction Manager,應該就差不多可以收工了。 - 沒得到 Response 的困境
如果有任意服務一直沒有在投票階段作出回應,那這段交易就會一直等待,直至 Timeout,嚴重影響效能。 - 投票成功後 commit 失敗
若投票階段是通過的,但在 commit 時有任一服務失敗了,這會造成「假成功」的問題,若想解決這問題我們還需要另一套工具來監控失敗。 - 資源大量被鎖
因為在做兩階段時,服務的資料都需要被鎖定以等待 commit,如果有多個請求在不斷出現,大量資源就會被鎖定,造成更多的等待。 - Scaling 問題
因為要依賴於 Transaction Manager,而 Transaction Manager 是有狀態 ( Stated ) ,因為需要記住現在的 Phase,所以我們無法輕易的水平擴充。這也違反了微服務該有的特性! - Throughput 降低
當流量都要等待對應的服務寫入,throughput 要通過 Transaction Manager,這樣自然會失去應對大流量的能力,所以只適合在小規模的服務上。
Saga Pattern
Saga 這機制中有兩個重要的元件,分別是 Sage Log 與 SEC ( Saga Execution Coordinator)。Saga 會將一個交易拆分為數個子交易,並透過 Saga Log 對這些請求進行追蹤。而 SEC 則是主要處理 Saga 的元件,它會接收 Saga 送出的請求,並讀取及更新 Saga Log。SEC 會對剛剛送出的請求進行等待回應,並對失敗的回應進行處理。
在上圖中,在拿到 R2 時,SEC 把 R2送到 service 2,然後 service 2 回傳出 R2 failed 的回應。這時 SEC在拿到 R2 failed 時,它會馬上開始發送一個補償請求 ( Compensating Request ),而補償請求是為了發送還原操作通知給所有已完成操作的服務,也就是 Roll Back,以防止在某操作失敗後的資料不一致問題。另外,在收到某一服務錯誤時,之後的操作就會中斷並開始進行補償請求。
Saga Log 可以是一個 Database,然後 SEC 透過對 DB 查詢或 DB 推送來獲得新的請求。 SEC 對 Service 的請求都可以透過簡單的 REST API 來實現,對於一般的 Saga 請求 SEC 應只發送一次,但對補償請求則可以發送多次,以確保還原成功。
Eventual Consistency Pattern
Eventual Consistency Pattern 的精神在於「資料會在最終變得一致,但不見得要馬上一致」。這樣的設計方式是基於 BASE 交易模型,不同於 ACID 模型著重於提供一個一致性系統,BASE 模型著重於高可用性。這表示了交易的更新是在背景中完成,而不是在交易時立即完成。而這種做法讓效能得到提升,因為資源不會因為交易被大量鎖住,但是也無法立即響應更新。
Eventual Consistency Pattern 很適合用於 long running、在背景執行的任務,對於 Race Conditions 的管理也變得相對簡單。在微服務的世界,我們常常會使用 Event Based 來做交易/行動,而事件模式正正與 Eventual Consistency Pattern 相輔相成,在事件被送出後就不會再被理會,一切都在背景中執行,而在非同步設計中,一致性也不是最重要的事情!
如果你覺得我的文章幫助到你,希望你也可以為文章拍手,分別 Follow 我的個人頁與程式愛好者,按讚我們的粉絲頁喔,支持我們推出更多更好的內容創作!