微服務權衡

許多開發團隊發現微服務架構風格優於單體架構。但其他團隊發現它們會降低生產力。與任何架構風格一樣,微服務會帶來成本和好處。要做出明智的選擇,您必須了解這些內容並將其應用於您的特定情境。

2015 年 7 月 1 日

    微服務提供的好處…

  • 強大的模組邊界微服務強化模組結構,這對於較大的團隊特別重要。
  • 獨立部署簡單的服務更容易部署,而且由於它們是自主的,因此出錯時不太可能導致系統故障。
  • 技術多樣性使用微服務,您可以混合多種語言、開發框架和資料儲存技術。

    …但會帶來成本

  • 分佈分散式系統較難編寫程式,因為遠端呼叫很慢,而且總是有失敗的風險。
  • 最終一致性對於分散式系統而言,維持強一致性極為困難,這表示每個人都必須管理最終一致性。
  • 運作複雜性您需要一個成熟的運作團隊來管理大量服務,這些服務會定期重新部署。

總結

強大的模組邊界(優點)

微服務的第一個重大好處是強大的模組邊界。這是一個重要的優點,但卻很奇怪,因為理論上沒有理由說微服務應該比單體架構有更強大的模組邊界。

那麼我所謂的強大模組邊界是什麼意思?我想大多數人會同意將軟體劃分為模組是件好事:彼此解耦的軟體區塊。您希望模組運作的方式是,如果我需要變更系統的一部分,大部分時間我只需要了解系統的一小部分即可進行變更,而且我可以很容易地找到那一小部分。良好的模組結構在任何程式中都很有用,但隨著軟體規模的擴大,它變得越來越重要。或許更重要的是,隨著開發團隊規模的擴大,它變得越來越重要。

微服務的倡導者很快地引入了康威定律,這個概念認為軟體系統的結構反映了建立它的組織的溝通結構。對於較大的團隊,特別是如果這些團隊位於不同的地點,則必須將軟體結構化,以認識到團隊間的溝通將比團隊內的溝通更不頻繁且更正式。微服務允許每個團隊照顧具有這種溝通模式的相對獨立的單元。

正如我先前所說,沒有理由讓單體系統沒有良好的模組化結構。[1]但許多人觀察到這似乎很罕見,因此大泥球是最常見的架構模式。事實上,對單體的常見命運感到沮喪,正是促使許多團隊採用微服務的原因。使用模組進行解耦之所以有效,是因為模組邊界是模組之間參考的障礙。問題在於,對於單體系統,通常很容易繞過障礙。這樣做可能是快速建立功能的有用策略捷徑,但廣泛執行會破壞模組化結構並破壞團隊的生產力。將模組放入個別服務會讓邊界更堅固,讓這些有害的解決方法更難以找到。

這種耦合的一個重要面向是持續資料。微服務的主要特徵之一是分散式資料管理,它表示每個服務管理自己的資料庫,任何其他服務都必須透過服務的 API 才能取得資料。這消除了整合資料庫,而整合資料庫是較大型系統中嚴重耦合的主要來源。

重要的是要強調,在單體中擁有穩固的模組邊界是完全可能的,但這需要紀律。同樣地,你可以得到一個微服務大泥球,但你需要付出更多努力才能做錯事。我認為,使用微服務會增加你獲得更好模組化的機率。如果你對團隊的紀律有信心,那麼這可能會消除這種優勢,但隨著團隊的成長,保持紀律會越來越困難,就像維護模組邊界變得越來越重要一樣。

如果你沒有正確設定邊界,這個優勢就會變成一個障礙。這是單體優先策略的兩個主要原因之一,也是為什麼即使是那些更傾向於早期執行微服務的人也會強調你只能在了解領域的情況下執行微服務。

但我對這一點的警告還沒有結束。你只能在一段時間後才能真正了解系統如何維護模組化。因此,我們只能在看到至少存在幾年的微服務系統後,才能真正評估微服務是否能帶來更好的模組化。此外,早期採用者往往更有才華,因此在我們評估由一般團隊編寫的微服務系統的模組化優勢之前,還需要進一步延遲。即便如此,我們必須接受一般團隊編寫一般軟體的事實,因此與其將結果與頂尖團隊進行比較,我們必須將產生的軟體與其在單體架構下的情況進行比較,而這是一個難以評估的反事實。

我目前只能根據我認識且使用這種風格的人提供的早期證據來繼續。他們的判斷是維護模組顯著容易得多。

有一個案例研究特別有趣。該團隊做出了錯誤的選擇,在一個複雜度不足以涵蓋微服務溢價的系統上使用微服務。該專案陷入困境,需要救援,因此有更多人投入該專案。在這個時候,微服務架構變得有幫助,因為系統能夠吸收開發人員的快速流入,而且團隊能夠比單體更輕鬆地利用更多團隊成員。因此,該專案加速到比單體預期更高的生產力,使團隊能夠迎頭趕上。結果仍然是淨負面,因為與採用單體相比,軟體的員工工時成本更高,但微服務架構確實支援擴充。

分佈(缺點)

因此,微服務使用分佈式系統來改善模組化。但分佈式軟體有一個主要的缺點,那就是它被分佈。一旦你使用分佈式,你就會遇到許多複雜性。我不認為微服務社群對這些成本像分佈式物件運動那樣天真,但複雜性仍然存在。

其中第一個是效能。你必須處於非常不尋常的位置,才能看到現今處理函式呼叫變成效能熱點,但遠端呼叫很慢。如果你的服務呼叫六個遠端服務,每個服務再呼叫六個遠端服務,這些回應時間加起來會產生一些可怕的延遲特性。

當然,你可以採取許多措施來減輕這個問題。首先,你可以增加呼叫的細緻度,這樣你就能減少呼叫次數。這會讓你的程式設計模型變得複雜,現在你必須考慮如何批次處理你的服務間互動。這也只會讓你進展到某個程度,因為你至少必須呼叫每個協作服務一次。

第二個緩解方法是使用非同步。如果你並行執行六個非同步呼叫,現在你的速度只會和最慢的呼叫一樣慢,而不是它們的延遲總和。這可能會帶來很大的效能提升,但會帶來另一個認知成本。非同步程式設計很困難:很難做對,而且更難除錯。但我聽過的大多數微服務案例都需要非同步才能獲得可接受的效能。

緊接在速度之後的是可靠性。你期望處理中的函式呼叫可以正常運作,但遠端呼叫隨時都可能失敗。有了大量的微服務,潛在的故障點就更多了。明智的開發人員知道這一點,並會設計以應對失敗。幸運的是,你為非同步協作所需的策略類型也適用於處理失敗,而且結果可以提高復原力。然而,這並不能帶來太多補償,你仍然需要額外的複雜性來找出每個遠端呼叫失敗的後果。

而這只是分散式運算的謬誤的前兩個。

這個問題有一些注意事項。首先,許多這些問題會隨著單體的成長而出現。很少有單體是真正獨立的,通常會有其他系統(通常是舊系統)需要協作。與它們互動涉及通過網路,並會遇到這些相同的問題。這就是為什麼許多人傾向於更快速地轉向微服務來處理與遠端系統的互動。這個問題也是經驗有助益的,一個更有技能的團隊將更能應對分發的問題。

但是,分發永遠都是一種成本。我總是猶豫是否要打出分發牌,而且我認為太多人因為低估了問題而過快地進行分發。

最終一致性(缺點)

我敢肯定你知道需要一點耐心的網站。你對某個東西進行更新,它會重新整理你的螢幕,但更新不見了。你等了一兩分鐘,按一下重新整理,它就在那裡了。

這是一個非常令人惱火的可用性問題,而且幾乎可以肯定是由於最終一致性的風險造成的。你的更新已由粉紅色節點接收,但你的取得要求是由綠色節點處理的。在綠色節點從粉紅色節點取得更新之前,你會陷入不一致的視窗中。最終它會一致,但在那之前你會想知道是否出了什麼問題。

像這樣的矛盾現象已經夠令人惱火了,但它們可能會嚴重得多。業務邏輯最終可能會根據不一致的資訊做出決策,當發生這種情況時,要診斷出問題所在可能非常困難,因為任何調查都會在不一致的視窗關閉後很久才會發生。

微服務會引發最終一致性問題,因為它們堅持分散式資料管理。使用巨石架構時,你可以使用單一交易同時更新一堆東西。微服務需要更新多個資源,而分散式交易是不受歡迎的(有其原因)。因此,開發人員現在需要了解一致性問題,並找出在程式碼後悔之前如何偵測何時不同步。

巨石架構世界並非沒有這些問題。隨著系統成長,需要使用快取來提升效能,而快取失效是另一個困難的問題。大多數應用程式需要離線鎖定來避免長時間的資料庫交易。外部系統需要無法與交易管理員協調的更新。業務流程通常比你想像的更能容忍不一致,因為業務通常更重視可用性(業務流程長期以來本能地了解CAP 定理)。

因此,與其他分散式問題一樣,巨石架構並非完全避免不一致問題,但它們所受的影響較小,特別是當它們較小時候。

獨立部署(優點)

在這個產業中,模組化邊界與分散式系統的複雜性之間的取捨已經存在於我的整個職業生涯中。但有一件事在過去十年中顯著改變,那就是發佈到生產環境的角色。在 20 世紀,生產發佈幾乎普遍是痛苦且罕見的事件,需要日夜輪班才能讓一些笨拙的軟體發揮作用。但現在,熟練的團隊會頻繁地發佈到生產環境,許多組織實踐持續交付,讓他們可以每天多次發佈到生產環境。

微服務是第一個後 DevOps 革命架構

-- Neal Ford

這種轉變對軟體產業產生了深遠的影響,並且與微服務運動息息相關。許多微服務工作都是由部署大型巨石架構的困難所觸發,其中巨石架構的一部分的微小變更可能會導致整個部署失敗。微服務的一個關鍵原則是服務是組件,因此可以獨立部署。因此,現在當你進行變更時,你只需要測試和部署一個小型服務。如果你搞砸了,你不會讓整個系統崩潰。畢竟,由於需要設計故障,即使你的組件完全故障,也不應該阻止系統的其他部分運作,儘管會出現某種形式的優雅降級。

這種關係是雙向的。由於許多微服務需要頻繁部署,因此必須將部署工作做好。這就是為什麼快速應用程式部署和快速基礎架構配置是微服務先決條件的原因。對於任何超出基礎知識的內容,您都需要進行持續交付。

持續交付的極大好處是縮短了從構想執行軟體之間的週期時間。這樣做的組織可以快速回應市場變化,並比競爭對手更快推出新功能。

儘管許多人將持續交付列為使用微服務的原因,但必須提到即使是大型單體也可以持續交付。Facebook 和 Etsy 是兩個最著名的案例。還有許多案例顯示,嘗試的微服務架構在獨立部署時失敗,其中多個服務需要仔細協調其版本[2]。雖然我確實聽到許多人爭論說使用微服務進行持續交付容易得多,但我對此的信服程度低於它們對模組化的實際重要性——儘管模組化自然與交付速度密切相關。

營運複雜性(缺點)

能夠快速部署小型獨立單元對開發來說是一個很大的好處,但由於六個應用程式現在變成了數百個小型微服務,因此對營運產生了額外的負擔。許多組織會發現處理如此大量的快速變更工具的難度是令人望而卻步的。

這強化了持續交付的重要角色。雖然持續交付對於單體來說是一項有價值的技能,幾乎總是值得花費精力去獲得,但對於嚴肅的微服務設置來說,它變得至關重要。沒有持續交付所促進的自動化和協作,根本無法處理數十項服務。由於管理這些服務和監控的需求增加,營運複雜性也隨之增加。同樣,如果微服務在組合中,那麼對單體應用程式有用的成熟度水準就變得必要了。

微服務支持者喜歡指出,由於每個服務都較小,因此更容易理解。但危險在於複雜性並未被消除,它只是轉移到了服務之間的互連。這可能會導致營運複雜性增加,例如除錯跨服務行為的困難。良好的服務邊界選擇將減少此問題,但錯誤放置的邊界會使問題惡化。

處理這種營運複雜性需要許多新的技能和工具——最重要的是技能。工具仍然不成熟,但我的直覺告訴我,即使有更好的工具,微服務環境中技能的低門檻也更高。

然而,對更好技能和工具的需求並不是處理這些營運複雜性最困難的部分。要有效地完成所有這些工作,您還需要引入DevOps 文化:開發人員、營運人員和參與軟體交付的所有其他人員之間的更緊密協作。文化變革很困難,尤其是在規模較大且歷史較悠久的組織中。如果您沒有進行這種技能提升和文化變革,您的單體應用程式將受到阻礙,而您的微服務應用程式將受到創傷。

技術多元性(優點)

由於每個微服務都是一個獨立部署的單元,因此您在其中擁有相當大的技術選擇自由度。微服務可以用不同的語言撰寫、使用不同的程式庫,並使用不同的資料儲存。這允許團隊選擇適當的工具來執行工作,某些語言和程式庫更適合某些類型的問題。

對技術多元性的討論通常集中在最適合工作的工具,但微服務最大的好處通常是版本控管這個更平淡無奇的問題。在單體架構中,您只能使用程式庫的單一版本,這種情況通常會導致有問題的升級。系統的一部分可能需要升級才能使用其新功能,但無法升級,因為升級會損壞系統的另一部分。處理程式庫版本控管問題是隨著程式碼庫變大而變得越來越困難的問題之一。

這裡有一個危險,那就是技術多元性太多,導致開發組織不堪負荷。我所知道的許多組織都鼓勵使用有限的技術。這種鼓勵透過提供用於監控等事務的共用工具來支援,這使得服務更容易堅持使用少數共用環境。

不要低估支援實驗的價值。對於單體系統,早期關於語言和架構的決策很難逆轉。大約十年後,此類決策可能會將團隊鎖定在尷尬的技術中。微服務允許團隊嘗試新的工具,並且如果出現更好的技術,還可以一次一個服務地逐步遷移系統。

次要因素

我認為上述項目是需要考慮的主要權衡。以下是我認為不那麼重要的其他幾件事。

微服務的支持者經常說服務更容易擴充,因為如果一個服務獲得大量負載,您可以只擴充它,而不是整個應用程式。然而,我努力回想一份像樣的經驗報告,讓我信服與透過複製完整應用程式進行 cookie-cutter 擴充 相比,執行這種選擇性擴充實際上更有效率。

微服務允許您分離敏感資料,並對該資料增加更謹慎的安全性。此外,透過確保微服務之間的所有流量都是安全的,微服務方法可以讓漏洞利用變得更困難。隨著安全性問題越來越重要,這可能會轉變為使用微服務的主要考量因素。即使沒有這樣,主要單體系統建立獨立服務來處理敏感資料也並不少見。

微服務的批評者談論測試微服務應用程式比單體應用程式更困難。雖然這是一個真正的難題,也是分散式應用程式更複雜的一部分,但有 測試微服務的良好方法。這裡最重要的事情是嚴肅看待測試,與之相比,測試單體和測試微服務之間的差異是次要的。

總結

任何關於任何架構風格的通用文章都會受到 一般建議的限制 的影響。因此,閱讀這樣的文章無法為你做出決定,但此類文章可以幫助你考慮你應該考慮的各種因素。這裡的每項成本和收益對不同的系統會有不同的權重,甚至在成本和收益之間進行交換(在更複雜的系統中,強大的模組邊界是好的,但對於簡單的系統來說卻是障礙)你做出的任何決定都取決於將此類標準應用於你的環境,評估哪些因素對你的系統最重要,以及它們如何影響你的特定環境。此外,我們對微服務架構的經驗相對有限。通常只有在系統成熟之後,你才能判斷架構決策,並且在你了解在開發開始後幾年內使用它的情況後。我們還沒有關於長期微服務架構的許多軼事。

單體和微服務不是一個簡單的二元選擇。兩者都是模糊的定義,這意味著許多系統將處於模糊的邊界區域。還有一些系統不屬於任何類別。包括我在內的大多數人談論微服務與單體的對比,因為將它們與更常見的風格進行對比是有意義的,但我們必須記住,有一些系統無法舒適地歸入任何類別。我認為單體和微服務是架構空間中的兩個區域。它們值得命名,因為它們具有有趣的特徵,值得討論,但沒有明智的架構師將它們視為架構空間的全面分割。

儘管如此,一個似乎被廣泛接受的一般總結觀點是存在 微服務溢價:微服務對生產力施加了成本,而這只能在更複雜的系統中得到彌補。因此,如果你可以用單體架構管理系統的複雜性,那麼你不應該使用微服務。

但微服務對話的內容不應讓我們忘記推動軟體專案成功與失敗的更重要問題。人員素質、彼此合作的程度以及與領域專家的溝通程度等軟性因素,將比是否使用微服務產生更大的影響。在純技術層面上,更重要的是專注於乾淨的程式碼、良好的測試和對演化式架構的重視。


腳註

1: 有些人認為「單體」是一種侮辱,總是暗示著不良的模組化結構。微服務領域的大多數人並未如此,他們將「單體」純粹定義為建置為單一單位的應用程式。當然,微服務倡導者相信大多數單體最終會變成一團糟,但我不知道有誰會爭論不可能建置出結構良好的單體。

2: 獨立部署服務的能力是微服務定義的一部分。因此,合理地說,必須協調其部署的一組服務並非微服務架構。合理地說,許多嘗試微服務架構的團隊會遇到麻煩,因為他們最終必須協調服務部署。

進一步閱讀

Sam Newman 在他著作的第 1 章中列出微服務的優點清單(建置微服務系統的詳細資訊的必要來源)。

Benjamin Wootton 的文章,微服務 - 不是免費的午餐!在 High Scaleability 上,是最早且最好的微服務使用缺點摘要之一。

致謝

Brian Mason、Chris Ford、Rebecca Parsons、Rob Miles、Scott Robinson、Stefan Tilkov、Steven Lowe 和 Unmesh Joshi 與我討論了本文的草稿。