你想要的是模組,不是微服務

為何人人都把微服務掛在嘴邊?

你想要的是模組,不是微服務
Photo by frank mckenna / Unsplash
原文: You Want Modules, Not Microservices
作者: Ted Neward
tl;dr: 架構設計不容易,人們總愛追逐新概念,這些概念很快變成業界主流的「解決方案」,卻忽略了實際情況和細節。為了改善架構,業界往往不假思索地全盤接受。微服務就是最近的例子,我們該好好剖析一下,找出問題的癥結。

微服務的核心價值,據說包含⋯⋯

⋯⋯一堆好東西!

  • 「可擴展性」:「程式碼可以拆成小塊,獨立開發、測試、部署和更新。」
  • 「專注」:「…開發人員專注於解決商業邏輯和業務問題。」
  • 「可用性」:「後端資料必須隨時都能提供給各種裝置使用…。」
  • 「簡潔」:「簡化大型企業級應用程式的開發。」
  • 「回應性」:「…讓分散式應用程式可以依據交易負載調整規模…。」
  • 「可靠性」:「透過複製的伺服器群組,避免單點故障。在故障發生後,將應用程式恢復到正常狀態。」

這些聽起來是不是很耳熟?有趣的是,這六段引言中,有兩段來自微服務相關文章,兩段來自二十年前的 EJB 資料,另外兩段來自四十多年前的 Oracle Tuxedo 技術。你分得出來嗎?

我們這個產業很愛把同樣的宣傳詞翻來覆去地用。

「忘記過去的人,注定要重蹈覆轍。」── George Santayana,《理性生活》(1905 年)

關於微服務的炒作,有篇文章列出了 10 個擁抱微服務的理由:

  1. 符合巨量資料最佳實務。 微服務很適合資料管線架構,符合巨量資料的收集、引入、處理和傳遞方式。資料管線中的每個步驟都由一個微服務負責。
  2. 易於建置和維護。 單一用途的設計,代表小團隊就能勝任建置和維護。每個團隊可以跨職能,同時專精於特定微服務。
  3. 提升程式碼品質。 將解決方案模組化成獨立元件,有助於開發團隊一次專注一小部分,簡化了整體的編碼和測試流程。
  4. 簡化團隊協作。 不同於傳統服務導向架構( SOA )使用笨重的跨程序通訊協定,微服務使用事件串流技術,更容易整合。
  5. 實現即時處理。 微服務架構的核心是發布/訂閱框架,可以即時處理資料,提供即時輸出和洞見。
  6. 促進快速發展。 微服務透過模組化架構,重複利用程式碼和資料,更容易部署更多資料驅動的應用,創造商業價值。
  7. 提供更多元的輸出。 資料集通常需要以不同方式呈現給不同對象,微服務簡化了資料擷取的方式,方便不同使用者使用。
  8. 更容易評估應用程式生命週期的更新。 進階分析環境(包含機器學習)需要評估新舊計算模型。微服務架構中的 A/B 測試和多變量測試,讓使用者可以驗證更新後的模型。
  9. 提升擴展性。 擴展性不只是處理更多流量,也攸關所需的工作量。微服務更容易找出擴展瓶頸,並在個別微服務層級解決。
  10. 有許多熱門工具可用。 巨量資料領域的許多技術(包含開源社群),都很適合微服務架構。 Apache Hadoop 、 Apache Spark 、 NoSQL 資料庫和許多串流分析工具都可以用在微服務上。

讓我們回顧一下過去的經驗,重新檢視這些論點:

  1. 符合巨量資料最佳實務。 管線和過濾器架構從 70 年代就存在了,當時 Unix 提倡了幾個概念:
    1. 每個程式做好一件事。做新工作時,重新打造一個程式,而不是在舊程式上加新功能。
    2. 每個程式的輸出,都應該可以作為另一個程式的輸入。輸出不要包含多餘資訊。避免嚴格的欄位式或二進位輸入格式。不要堅持互動式輸入。
  2. 易於建置和維護。 請參考上面的 Unix 哲學。
  3. 提升程式碼品質。 如果一次專注一小部分可以提升品質,請參考上面的 Unix 哲學。
  4. 簡化團隊協作。 這點很有意思,它暗示「服務導向架構( SOA )…通常使用笨重的跨程序通訊協定」──例如透過 HTTP 傳輸 JSON ?還是指所有 SOA 都需要 SOAP 、 WSDL 、 XML Schema 和一堆 WS-* 規範?諷刺的是,微服務並沒有限制你使用這些「笨重」的協定,有些微服務甚至建議使用 gRPC ,一種更接近 CORBA 的 IIOP 的二進位協定,而 IIOP 正是 SOAP 、 WSDL 、 XML Schema 和一堆 WS-* 規範的前身。
  5. 實現即時處理。 即時處理早就不是新鮮事了,雖然很多這類系統都使用發布/訂閱或「事件匯流排」模型,但这跟微服務沒有直接關係。
  6. 促進快速發展。 「重複利用模組化架構」──到底有多少技術都把「重複利用」當成賣點?程式語言(物件導向、函數式、程序式)、函式庫、框架…真希望哪天能看到一種技術,直接說:「重複利用?誰在乎!」
  7. 提供更多元的輸出。 「資料集通常需要以不同方式呈現給不同對象」──這聽起來像是 Crystal Reports 的廣告詞。
  8. 更容易評估應用程式生命週期的更新。 機器學習和進階分析環境需要「評估新舊計算模型」…感覺像是把一堆專業術語湊在一起,沒什麼實質意義。
  9. 提升擴展性。 真有趣, EJB 、交易中介軟體(例如 Tuxedo )和大型主機也都這樣說過。
  10. 有許多熱門工具可用。 每個熱門技術都有相對應的工具,尤其是在熱潮退燒之後。大多數讀者可能太年輕,沒聽過 CASE 工具,但或許還記得 UML

仔細想想,上面有一半的論點,都圍繞著一個共同主題──建立和維護小型、獨立的程式碼和資料區塊,各自版本控管,使用共同的輸入和輸出,以便整合到更大的系統中。這不就是…

微服務的核心,其實是⋯⋯

⋯⋯模組。

沒錯,就是不起眼的「模組」,這個核心概念從 1970 年代就存在於大多數程式語言中。(更早之前也有,只是當時的語言沒有把模組當成一級概念,比較難實現。)在 CLR 上叫「組件」( C# 、 F# 、 Visual Basic …),在 JVM 上叫「 JAR 」或「套件」( Java 、 Kotlin 、 Clojure 、 Scala 、 Groovy …),作業系統上的動態連結函式庫( Windows 的 DLL 、 *nix 的 so a , macOS 的「框架」藏在 /Library 目錄裡),概念上都一樣,都是模組。它們內部格式不同,但目的相同:一個可以獨立建置、管理、版本控管和部署的程式碼單元,可以重複使用。

參考一下電腦科學的經典論文,對模組的定義:

「專案工作的明確分段,確保了系統的模組化。每個任務都形成一個獨立、明確的程式模組。在實作階段,每個模組的輸入和輸出都定義清楚,介面一目瞭然。在測試階段,每個模組可以獨立測試,減少整合測試的排程問題。最後,系統以模組化的方式維護,更容易找出錯誤,縮小除錯範圍。」

這段話出自 David Parnas 的經典論文《論分解系統為模組的標準》,寫於 1971 年──距今超過 50 年。所謂「獨立、明確的程式模組」,涵蓋了微服務一半的優點,而且我們已經這樣做了五十年。

那為什麼還要炒作微服務呢?

因為微服務的重點,從來就不是微服務本身,也不是服務,甚至不是分散式系統。

微服務的重點,應該是⋯⋯

⋯⋯組織的清晰度。

亞馬遜是最早公開討論微服務概念的公司之一,他們的重點不在技術架構,而在於打造獨立的開發團隊,減少團隊之間的依賴。等 DBA 修改資料庫?等 QA 測試?等 infra 團隊買伺服器?等 UX 團隊做原型?

咻咻咻咻咻…

你聽到的聲音,是開發團隊把所有會卡住他們的依賴項,都攬到自己身上。這代表團隊變成一個小型的 IT 部門,包含分析、開發、設計、測試、資料管理、部署、維運等等。這也代表團隊成員必須具備各種技能,或者每個成員都要是「全端工程師」,這讓招募變得更困難。同時,團隊也要為線上服務負責,必須隨時待命(當然也包含額外的薪資和法律問題)。但如果這些都克服了,團隊就可以獨立開發,唯一的限制就是時間和打字速度。

理論上是這樣。

微服務的真相,常常是⋯⋯

⋯⋯分散式運算謬誤

不熟悉的人,可以參考 Peter Deutsch 在 80 年代提出的概念。 1994 年 Ann Wolrath 和 Jim Waldo 的經典論文《分散式運算註記》也提到類似的概念:

「要做好分散式系統(效能、可靠性、擴展性等等),很難。」

如果系統是由單一機器上的模組組成,即使是五十年前,跨程序或函式庫傳輸資料的成本也很低。但如果透過網路傳輸資料(大多數微服務都這樣做),延遲會增加 5 到 7 個數量級。這不是靠加機器就能解決的,反而會讓問題更糟。

把微服務放在同一台機器上,例如用 Docker Compose 或 Kubernetes 管理一堆 Docker 容器,可以減少部分影響。但這會增加虛擬機器之間的延遲(因為資料需要在虛擬網路堆疊中傳輸,即使有些網路層是虛擬的),而且單點故障的問題依然存在。

更糟的是,除了分散式運算謬誤,我們還會遇到另一個問題:企業運算謬誤

微服務的根本之道,在於⋯⋯

⋯⋯重新思考我們真正的需求。

你需要把問題拆成獨立的單元嗎? 你可以用 Docker 容器,也可以用應用程式伺服器裡的模組,搭配標準化的 API ,還有很多其他選擇。這不是技術問題,不需要捨棄現有的技術──過去二十年的技術都可以用,包括 servlet 、 ASP.NET 、 Ruby 、 Python 、 C++ ,甚至 Perl 。重點是建立一個通用的架構基礎,定義清楚的整合和通訊規範。

你需要減少開發團隊的依賴嗎? 那就先找出是哪些依賴,和相關團隊合作,看看哪些可以自己處理。如果公司不想打破「技能導向」的組織架構(例如有獨立的資料庫團隊、 infra 團隊、 QA 團隊),那就和主管討論,建立「虛線」的回報機制,讓每個團隊都有人可以支援開發團隊。最重要的是,團隊必須清楚知道目標是什麼,並且能向任何人清楚地解釋他們的服務/微服務/模組的核心價值。重點是給團隊明確的方向和目標,讓他們自主完成任務。

說到底,就這兩件事,它們之間其實沒什麼關係。