戳破微服务的七大谎言
在現代技術公司(無論大小)的架構中,微服務已經無處不在。但是,它們真的比以前的開發模型更優秀嗎?在這篇文章中,我將揭穿工程師們關于微服務所講述的七大謊言,以及為什么它可能是一種反模式。
免責聲明 1:我不是架構師,也沒假裝自己是架構師。本文內容只是我多年來作為軟件開發人員 / 經理所做的觀察總結。我曾見證兩家公司在微服務架構的壓力下陷入泥潭。由于很少有人深度質疑這種新生范式,因此我想表達自己的聲音。不過,我經驗有限,所以也歡迎反饋意見。
免責聲明 2:互聯網上也有其他標題相似的演講 / 文章,這里就不糾結這種相似度了。
單體架構和微服務之間有何區別?
開始研究謊言前,我們先來定義一下術語。后端軟件架構可以分為單體和微服務兩種。單體架構指的是由一臺或多臺服務器運行單個應用程序,其通常從單個存儲庫中部署。使用多臺服務器時,這些服務器將運行相同的代碼。從 90 年代到 2000 年代,多數情況下這都是默認的架構。
隨著互聯網的發展,大型公司開始面臨單體架構的局限。為解決這一問題,公司開始將其代碼分割成在不同服務器上運行的多個組件。例如,一家公司可能會有運行日志記錄的服務、調用外部 API 的服務以及管理數據庫的服務。亞馬遜 AWS 在這一風潮中扮演了重要角色,因為它讓部署服務器和管理基礎設施的工作變得非常容易。
隨著時間流逝,中小型公司也開始接受這種新的發展范式。很快,一個圍繞微服務的產業發展壯大起來,它逐漸成為尋求擴展的企業默認架構。不幸的是,現在有許多公司由于這種選擇而掉進了各種之前想不到的坑里。
1 謊言一:跨服務的關注點分離降低了復雜度
“[關注點分離]”指的是在不相關的代碼間應存在隔離墻。當不相關的代碼需要協同工作時,應該使用抽象良好的接口并盡量減少狀態共享。很多入門編程課程都將其視為標準的軟件開發公理。你的代碼對其他代碼的了解,越少越好。同樣,一個函數執行的功能越多,你就越需要考慮運動部件之間的復雜關系(即復雜度)。而且,我們作為合格的工程師就應該努力降低復雜度。
我堅信這一公理。從邏輯上講,分離關注點的最佳方法是否就是讓你的無關代碼運行在不同的服務(服務之間以 API 溝通)中呢?
不,并非如此。
經典的單進程關注點分離之所以有效,是因為它可以最小化并簡化不相關代碼之間的接口。在設計良好的程序中,此接口可以只有帶 return 語句的單個函數調用。不相關代碼之間的邊界本質上是復雜的,而簡單的接口有助于管理這種復雜性。
相比之下,在微服務中,函數調用被替換為網絡請求。這種新的服務間障礙嚴格來說更加復雜且更不可靠。首先,每個網絡調用都需要一定數量的樣板。其次,工程師現在需要默認任何服務隨時會失效。相反,在單體中,當代碼失敗時整個服務都會失敗。盡管這聽起來很糟糕,但由于現在只有一種故障情況,因此它更易于管理。
?在實踐中更糟
左下方的圖表是幾年前 Uber 的微服務架構。它很簡單,很容易理解。右邊是 Uber 的實際服務地圖。
我敢說 Uber 的任何人都不知道這個架構是如何工作的。曾在使用微服務架構的大型公司工作過的人都知道,Uber 的經驗并不是特例。
模型與現實之間存在這種差異的原因有兩個。首先,這些圖表通常過于簡化。架構師設計這些圖表是為了交流,而非完美反映現實。但因為它們隱藏了復雜性,也就容易誤導決策。如果 Uber 的技術領導者知道自己的架構會變成什么樣,他們還會走這條路嗎?
其次,一旦你投身于微服務,隨后的所有技術決策都將受其影響。因此,開發新功能時總要啟動新的服務。架構圖很快就會變得非常復雜,膨脹成上圖那種密密麻麻的網狀結構。
2 謊言二:微服務提高開發速度
當你采用“關注點分離”公理并將其應用在開發人員頭上時會發生什么?你會得到一些孤立的團隊,他們之間各自獨立。從表面上看這似乎是有益的。如果團隊只需要操心自己的服務,那將減輕他們的認知負擔,并提高他們的生產力。現在,工程師無需擔心基礎架構中其他部分的復雜性了。
問題在于,大多數新功能都需要一些跨多個服務的補丁。
許多功能需要在兩個或多個服務上開發
開發多服務功能需要在具有不同優先級和能力的團隊之間安排大量會議。考慮到他們從事的多個不同項目,這些團隊可能需要異步協作。你現在還需要交付經理來分配工作和管理迭代。
從技術角度來看,實現多服務功能可能需要編輯多個存儲庫。至少,它需要一種方法來測試在多個服務上運行的代碼。
對許多公司而言,這種多服務測試的需求是事后才會意識到的。架構師在設計技術棧時會假設大多數開發工作都將在單個服務上進行。多服務功能將很少見。你如何手動測試多服務功能?你需要在機器上啟動多個容器,并仔細設置每個容器的狀態。那單元測試呢?你將在哪里對多服務功能進行單元測試?是倉庫 A 還是倉庫 B?文檔寫好了嗎?部署往往會破壞未調整好的服務。很容易想象,數據流中的一個小錯誤會破壞多個下游服務。我們應該期望工程師理解所有可能依賴其代碼的下游服務嗎?
如果你的組織沒有投入大量的工程資源來構建多服務測試流程,那么除了最常見的功能之外,開發新功能的速度會像蝸牛般緩慢。如果沒有質量測試框架,看似簡單的任務(例如“添加分頁”)也可能會變成歷時數月、跨多個團隊的工作。
3 謊言三:部署眾多小型服務比部署整個應用更安全
“回滾”是現代軟件工程需要面對的現實。作為工程師,當你部署的代碼會破壞某些功能時,必須回滾部署并還原提交。沒有人想要回滾——尤其是部署代碼的工程師。但是,好的公司知道錯誤的部署總有可能出現,因此必須對其進行管理。
微服務架構的一個觀點是,部署多個獨立服務比部署整個應用更安全。當一項服務中斷時,其他服務還有回退可用。整個應用程序將繼續運行,客戶不會有什么感覺。
這種方法存在多個問題。
首先,這要假設你的服務可以容忍其他任何服務的隨機消失。這是 Netflix 的“Chaos Monkey”方法。但是,將其構建到服務中并非易事,測試它需要資源,并且除非這是工程的最優先事項,否則實踐中人們多大程度上會遵守這一要求就不一定了。
https://netflix.github.io/chaosmonkey/
其次,部署多服務功能時,服務的上線時間會有所不同。在一段時間里,你的那些服務將有不同的版本。對此有多種處理方法。你是否在半夜部署?你是否并行維護不同的 API 版本?你是否使用托管流?所有解決方案都需要額外的工程資源。如果部署意外破壞了(甚至不是部署的一部分)服務中的狀態,會發生什么情況?你是否有針對任何意外情況的預案?
雖然單體部署也會出錯,但是有多種方法可以緩解這種情況(藍色 / 綠色、金絲雀等等)。雖然這些方法也可用于微服務,但是設置和管理安全部署并非易事,應對一項服務總比應對多個服務要容易些。
4 謊言四:分開擴展服務通常是有利的
在每個應用程序中,都有經常運行的部分和很少運行的部分。很少運行的部件比頻繁運行的部件需要的資源要少一些。那么分開擴展這些部件是否有意義?
從根本上講,擴展軟件的原因是因為你的軟件需要更多的核心資源。這些資源可能是 CPU 周期、內存、磁盤空間或網絡。例如,當 CPU 以 100%運行時,可以啟動另一個服務來減輕壓力。
對于大多數應用,水平擴展(克隆單體)就足夠了。水平擴展的復雜度較低,許多云服務都可以用很少的配置來做到這一點。
相比之下,選擇分開擴展許多微服務有兩個常見原因。首先,如果你的代碼具有實質上并行的部分,則在某些情況下將計算塊分配給不同的“worker”可能會有些意義。重要的是,相對于每個任務的總計算量,數據傳輸和加速的開銷必須夠低。因此,將十個計算塊(每個計算耗時 10ms)發送到服務器,開銷卻為 100ms 就沒有并行的價值了。因為順序執行耗時是 10x10ms=100ms,而并行卻是 10ms+100ms=110ms。但是,如果每次計算都花費 100 毫秒,則將它們并行化就能節省時間。
其次,如果資源需求在整個請求中出現變化,則單獨擴展各個微服務可能是有意義的。例如,如果一個請求在開始時是受內存限制的,而在結束時是 CPU 限制的,那么就可以將請求的開始部分放在高內存服務中,將結束部分放在高 CPU 服務中。即便如此,除非你是獨角獸級別的企業,否則分開擴展服務帶來的財務優勢可能也無法抵消額外的復雜性。
另外,你試圖省錢的做法可能會適得其反:
快樂的云客戶
5 謊言五:微服務架構性能更高
我在學校學到了一些經驗法則:
-
讀取內存所需的時間是讀取二級緩存的 10 倍;
-
讀取硬盤驅動器所需時間是讀取內存的 10 倍;
-
從網絡讀取所需的時間是從硬盤讀取的 10 倍。
這些數字是粗略的近似值。我們來看一下數據中心中的網絡通信與從內存讀取之間的實際差異:2009 年,從內存中順序讀取 1MB 的耗時估計為 250000ns;2019 年,在 AWS 數據中心中,兩個 EC2 實例之間的通信速度可以達到 5Gbps。
http://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf
https://aws.amazon.com/blogs/aws/the-floodgates-are-open-increased-network-bandwidth-for-ec2-instances/
簡單算一算:
-
2009 年的內存:0.25 毫秒內 1 兆字節;
-
2019 年的數據中心:1 秒內 5Gbit=1 秒內 0.625GBytes=0.64 秒內 1 兆字節
覺得差距不算大?可我們要意識到:
上面的網絡速度是最好的情況;
我們正在對比 2009 年與 2019 年的指標;
要通過超高速的 AWS 網絡發送這一兆數據,我們仍然需要從內存中讀取它。
假設你只有一個依賴項,那么這也意味著幾千倍的速度差距。實際情況中這一差距還會大得多。
難怪我們現在使用字節流來讓每個請求快那么幾毫秒。當然,對于字節流來說,調試服務間通信也是需要工具的。
6 謊言六:管理多個服務并不難
軟件工程師喜歡自欺欺人。也許你以前聽過,什么“不需要很長時間”“請給我幾個小時”或“我可以在周末完成任務”,諸如此類。優秀的項目經理會理解這一點,并將工程師的估算值乘以四(或四十……)。
使用微服務的決策也會有同樣的樂觀情緒。這項工作并不是"為所有人獲取 AWS 證書并移動一些代碼"那么簡單。實踐中會有大量意外開銷。下面是你需要的一些人員和工具類別:
架構師。你需要一些人來繪制美觀且過于簡化的圖表并做演示。
發布管理。現在,你需要協調各個部署并管理多個 pipelines。這種協調工作將需要通用工具鏈,還要有團隊來維護這一工具鏈。
DevOps。“常規”工程師既沒有專業知識,也沒有意愿來正確配置他們的服務。他們也很難正確處理安全性問題。
數據工程師。如果你很幸運能夠按照這些建議來成功分解數據存儲,那么你現在需要一個團隊來將這些數據提取到一個地方進行分析。
配置文件。雖然一些額外的 YAML 文件聽起來并不那么糟糕,但這里會出現最危險的錯誤。它們也難以測試和調試。
https://abcnews.go.com/Technology/wireStory/latest-twitter-appears-back-outage-64276132
你不僅需要支付所有這些額外人員的薪酬,而且還指數級增加了工程組織中的溝通渠道數量。這會拖慢所有人的步伐。
7 謊言七:如果你從頭開始精心設計微服務,它們將會起作用
這里我引用一段文字:
正常運作的復雜系統一定是從一個正常運作的簡單系統演變而來的。從頭開始設計的復雜系統永遠無法正常工作,也無法靠打補丁來正常運作。你必須從一個簡單系統起步。——Gall 定律
8 總結
既然有這么多如此明顯的缺點,為什么微服務還這么受歡迎呢?
我相信大多數工程師(包括我本人)都有一定程度的自我能力否定傾向。很多時候,我們需要面對自身能力不足以應付的狀況,卻依舊要跨過眼前的障礙。在這種情況下,依靠他人的成果和“最佳實踐”是更安全的。但是,我們很快就認為這些“最佳實踐”是經過深思熟慮的,或肯定適用于我們的問題。當你啟用更多服務時,云供應商會受益。微服務倡導者在你購買他們出的書時也會賺錢。他們倆都有動力向你兜售你本來用不到的技術。
不管怎樣,我認為在某些情況下微服務可能是正確的選擇。如果你是谷歌或 Facebook 那樣的企業,并且要應對數十種產品上數以十億計的活躍用戶,那么單體架構肯定是不夠的。如果你有大量可并行化的任務,那么只用單體也是不行的。
我的目的是要告訴大家,后端服務設計是非常重要的,沒有哪種選擇是銀彈。無論我們是在談論微服務還是單體,SQL 還是 NoSQL,Python 還是 Node,本質都一樣。任何技術都不可能完美適應所有用例。
因此,你應該認真思考各種想法,質疑所有假設并清醒地做出架構決策。你的選擇可能會成就或拖垮你的公司。
原文鏈接:
https://scottrogowski.com/the-seven-deceptions-of-microservices
總結
以上是生活随笔為你收集整理的戳破微服务的七大谎言的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 想理解Java的IO,不要从操作系统开始
- 下一篇: Spring Cache 缺陷,我好像有