《單體式系統到微服務》讀後分享 - 下

近日公司要開始大力導入微服務,趕緊惡補一些相關知識,這本書一開始提了一些「不該導入微服務」以及思考微服務的替代方案,後續提供導入微服務的幾種模式,尤其是資料庫的部分

第四章:分解資料庫

上一章是用系統的層級討論如何拆分,但系統最難拆分的部分無疑是 資料庫,所以作者花了很大的篇幅描述多種資料庫拆分的方式

分解模式

模式一:共享資料庫

所有的服務共享一個資料庫,這是一個不太好的選擇,因為無法保證誰控制了資料,每個服務都有讀寫、修改 Schema 的能力,這暗示了缺乏商業邏輯的凝聚力

模式二:資料庫視圖

如果仍希望多個服務間共享一個資料庫,可以透過視圖 (View Table) 減輕耦合上的困擾,因為視圖可以有獨立的權限管理並隱藏底層實際儲存的邏輯; 視圖的功能取決於資料庫本身的限制,要小心資料是否過時等問題

模式三:資料庫包裝服務

將資料庫隱藏在服務後面,從資料庫依賴轉變成服務依賴,這可以當作轉移的第一步,先避免資料庫耦合持續惡化,透過一層服務封裝與隱藏實際的資料庫邏輯

書中案例是銀行的權限管理,多個服務因應不同的場景持續堆疊新的權限管理在資料庫中,透過拆分出權限服務,將權限調整限縮單一服務,後續再考慮拆分獨立資料庫,有點像上一章提到的抽象分枝

跟視圖相比的好處是不需要映射現有的資料表,且可以透過程式邏輯控制更複雜的操作,只是上游介接的服務需要調整

模式四:資料庫服務介面

如果客戶端是 BI 工具需要直接透過 SQL 端點讀取,最好是建立專門的資料庫作為公開的讀取端點,對映引擎負責同步資料,可以透過批次處理、串流同步工具 (作者推薦 Debezium),處理的複雜度仰賴於內部資料庫與對外資料庫的差異,如從 Cassandra 映射到 SQL DB

先分割資料庫還是程式碼

先分割資料庫

要小心跨資料庫 Join、一致性與完整性問題,如果要特別注意資料一致性問題可以採取此方法,缺點是短期效益不大,依然要面臨單體式架構

可以透過程式中的 Storage Layer 將 Domain 對應到獨立的 Table / Database

先分割程式碼

先分割程式碼更容易了解新服務需要的資料,也能夠提早獨立部署新服務,但有可能拆分完程式碼就沒有繼續拆分資料庫

資料一致性

如果需要針對不同的 Domain 操作可以在 SQL 資料庫中用 Transaction 包起來確保原子性,但是分散式架構就需要用別的方式確保原子性

方法一:二階段提交

透過一個協調者,與多個服務溝通並確保大家一起 commit 或一起 rollback,演算法複雜且效率差,如果居中的協調者失敗則會出現非一致性問題

方法二:Saga

避免長時間鎖定資料,將每個執行步驟模組化獨立運作,個別服務都要處理異常事件時的補償行為,參考 Saga 分散式交易模式,但要小心 Saga 模式的複雜度很高

第五章:成長過程中的痛苦

導入微服務會遇到很多的挑戰,尤其是當團隊人數與微服務數量持續增加時,可能會開始出現以下問題

1. 大規模所有權

當要改動程式碼時,會依照程式碼所有權概念而有所不同,如果是強大的程式碼所有權則需要再修改前告知持有者;弱所有權或是集體所有權則是直接修改不需要額外通知

通常一開始都是集體所有權,大家分工開發而沒有特定的任務,但是當人數超過 100 人,通常好的團隊都是強所有權,讓每個團隊專注於特定領域

2. 重大變革

當微服務改動時,如果沒有謹慎思考破壞原本的介面,則相依的服務也會跟著被破壞,例如提升 API 版號時,記得要公告並保留一定的支援時間

3. 報告

過往單體式資料庫要拉報表相對簡單,但如果拆分微服務,不同團隊採用不同的資料庫,要確保最終再產生報告時有同步相同的資料

4. 監控和疑難排查

分散式系統的監控與排查難度會比單體式高非常多,可以使用 ELK / Fluentd 等技術彙整,並增加系統的可觀測性,Observability 的概念算是把 Monitoring 再往前推進一步,可以參考 喬叔帶你上手 Elastic Stack - 探索與實踐 Observability:01 - 前言 & 淺談 Observability;在追蹤部分,可以把每一個呼叫都產生對應的關聯 ID,追蹤在不同微服務間的通信,可以參考開源工具 Jaeger

5. 當地開發者經驗

當有更多服務時,要在本地端開發變成一場夢魘,雖然說有 Docker 等容器化技術,但如果像 JVM 需要吃大量資源,可能會耗盡開發者的本地端資源,如果是使用 k8s 可以考慮用 Telepresence,部署完整服務在雲端,開發者可以運行特定服務在本地端其餘依賴雲端的服務

6. 全域與本地優化比較

雖然說微服務可以讓各個團隊彈性的採用技術,但從整體的角度太過分散的技術棧可能會導致費用上或是維護上的困難;但如果太集中化管理,強迫各個團隊採用相同的技術又失去了微服務的部分好處

這可以用前幾章描述的可逆與不可逆決策,如果是不可逆如雲端供應商等,則需要跨團隊的討論,但可逆的決策如採用函式庫、嘗試新的程式語言則可以放手給團隊決定,最好是可以讓每個團隊出一名技術負責人組成跨部門小組,同時兼容全域與本地優化的可能

總結

以上是從書中摘錄的內容,因為還沒有太多的實際經驗,只能照本宣科,無法像安德魯大叔寫出抽象化程度夠也包含足夠扎實的經驗分享,期許自己未來有更多經驗能夠重新補充文章內容

好的架構設計是持續演進的,建議是從 Monolithic -> Modulized Monolithic -> Microservices,如果無法模組化單體式,那只會得到更糟糕的微服務,微服務本身透過獨立的服務形成高強度、難以違背的模組化,問題就回到程式設計的最源頭如何設計出高內聚+低耦合的模組,往下預計還有幾個知識點的延伸 DDD / Event Storming / Clean Architecture / Observability (遠望

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus