1. 程式人生 > >Martin Fowler談微服務的優缺點

Martin Fowler談微服務的優缺點

【編者的話】本文作者Martin Fowler是軟體開發和設計領域的專家。微服務已經成為當下架構設計的熱門話題,本文分析了微服務的優缺點,特別指出了當對微服務做出取捨時需要考量的各種因素,寫的很細緻,發人思考。

很多開發團隊已經認識到微服務架構比單體架構更優越。但是也有其他團隊感覺到這是一種消弱生產力的負擔。就像任何軟體架構,微服務架構同樣有利弊。為了能做出一個明智的選擇,你必須瞭解這些應用並將它們運用到你特定的環境中。
微服務.png

模組化

微服務最大的好處是對功能模組的劃分。這是個既重要又奇怪的好處,因為理論上沒有理由來解釋為何微服務比單體架構有更強大的模組劃分。

那到底什麼是我說的模組邊界。我想大多數人同意,將軟體劃分成多個模組,非常好:彼此就可以分離解耦。如果我需要改變系統的一小部分,大多數的時候我只需要弄清楚這一小塊,然後做些改動,我們會發現小變動真的很容易。對於任何程式而言,好的模組化結構非常有用,特別是當軟體的規模變大,模組化會顯得格外重要。也許,它會變得更重要,因為團隊的規模也在增加。

提倡微服務,需要快速介紹一下
康威定律
,該定律認為一個軟體系統的結構反映了團隊的溝通結構。對於較大的團隊,特別對於那些分散在各地的團隊,對軟體實施結構化顯得尤為重要,跨團隊之間的溝通與一個團隊內部溝通相比,將會變得不那麼頻繁且更加正式。微服務允許每個團隊用類似的溝通模式來照看相對獨立的模組單元。

正如我前面所說,沒有理由可以解釋為何一個單體系統不應該擁有一個良好的模組化結構[1]。但是很多人都觀察到這似乎很少見,因此大雜燴成了最常見的架構模式。事實上單體架構這種固有的困惑,驅動一些團隊開始使用微服務架構。模組的解耦能夠起作用,是因為模組的邊界就是模組之間引用的障礙。麻煩的是,一個單體系統,通常都很容易繞過障礙。這樣做,可能是構建功能時有用的技術捷徑,可是呢,如果廣泛運用這種方法,將削弱模組化的結構和降低團隊的生產力。將模組放入獨立的服務,這樣做使模組的邊界更為牢固,使這種自殺式的解決方案更難實現。

耦合的重要部分是持久化的資料。微服務的關鍵特徵是
去中心的資料管理
,也就是說每個服務管理它自己的資料庫,其它的服務必須通過該服務的API來互動。這就會消除整合資料庫,這是大系統非常討厭耦合的主要原因。

值得強調的是在一個單體系統中,使用模組化完全可能,但是它需要紀律來保證。同樣地,你也可能得到微服務的大雜燴模式,但是對於本身已是微服務的架構,要犯這樣的錯還是不太容易的。在我看來,使用微服務,可以幫助你實現更好的模組化。如果你對團隊紀律很有信心,那麼可能會消除這一優勢,不過,隨著團隊的規模增加,保持紀律會變得越來越,這也就意味著維護模組邊界將顯得更為重要性。

如果你不好好管理模組的邊界,這個優勢就會變成障礙。這就是單體優先策略的兩個主要原因之一,也是為何我更傾向於
較早使用微服務
,你只能在一個領域瞭解得非常透徹。

但我現在不會在這方面做出警告。只有經過時間的洗禮,你才能真正講出要如何才能把一個系統的模組化維護好。所以只有我們看到微服務系統已經執行好多年,才可以評估它是否會帶來更好的模組化特性。而且,早期接納者更有才華,也就是說在我們能夠評估一般團隊構建的微服務系統的模組化優勢之前,還有好幾年的延遲。所以與其和高水平的團隊比較成果,還不如比較那些已經使用單體架構的軟體帶來的變化,這是一個棘手的違背事實的評估。

所有我現在能做的,就是聽聽那些我認識的人,他們使用微服務架構的早期經驗。他們的判斷就是微服務能讓他們更容易去維護功能模組。

有一個案例特別有意思。有一個團隊做了一個錯誤的決定,在一個不那麼複雜的系統上面使用微服務。後來這個專案碰到了問題,需要幫助,然後很多人就跑了進來。這方面,微服務還是有所幫助的,因為這個系統能夠吸引開發者的快速湧入,和單體架構相比,團隊更容易擴張。最終這個專案的進度大大超過使用單體架構的預期,使整個團隊趕上進度。當然也有負面的,和單體架構相比,微服務的方式耗費了更多的人力,但是微服務的架構能夠支援未來更大的規模。

如果你想要知道更多有關於如何構建微服務系統,Sam Newman的這本書是很重要的資料。

分散式特性

微服務採用分散式系統來提高模組化。但是分散式軟體有一個主要的缺陷,就是分散式系統本身。一旦你開始玩分散式系統,你就會碰到一堆複雜的問題。我不認為微服務社群對分散式系統所帶來的成本沒有概念,但是複雜性也確實存在。

首先是效能。這時候,你不得不以一種不常見的方式,看著程序內的函式呼叫轉變成效能的瓶頸,遠端呼叫是很慢的。如果你的服務呼叫了很多遠端服務,這些遠端服務本身也要呼叫了另外一些遠端服務,這些響應時間加起來,就會帶來很恐怖的延遲特徵。

當然你有辦法減少延遲。首先,你可以增加呼叫的粒度,減少呼叫的數目。這會使你的程式設計模式變得複雜,使得你必須想清楚如何批量處理跨服務互動。由於你必須呼叫至少一次這些所有合作的服務,因此到目前為止,你能做的就這麼多了。

第二種方法就是使用非同步通訊。如果六個服務非同步並行呼叫,延遲只會是那個最慢的呼叫,而不是所有呼叫延遲的總和。這大大改善了效能,但也帶來了認知成本。非同步程式設計很難,你很難用好它,而且很難除錯。但我聽到的大多數微服務的例子,都需要非同步來獲得預期的效能。

速度之後就是可靠性的問題。你期望in-process函式呼叫能夠成功,可是一個遠端呼叫可能在任何時間失敗。在很多的微服務中,甚至有很多的潛在的故障點。明智的開發人員知道這些,為可能發生的故障事先設計。你為非同步協作做的方案,也適應於故障處理,還可提高服務的彈性。然而,這些補償仍然不夠,仍然有著額外的複雜性,你需要弄明白每一個遠端呼叫失敗的後果。

這就是分散式計算最主要的兩個難題

對於這個問題,還有一些要注意的地方。首先,很多這類的問題出現在單體架構規模擴充的時候。很少有單體架構可以真正獨立執行,通常是跟其他系統一起,這些系統大部分是遺留系統。和這些系統通過網路進行互動,同樣會碰到這些類似的問題。這就是為何很多人都傾向於更快轉移到微服務架構,來處理遠端系統的互動問題。這個問題,同樣也和經驗有關係,更熟練的團隊更有能力處理分散式特性所帶來的問題。

當然,分散式特性永遠都是一個成本。我總是不太願意打分散式這張牌,因為想到很多很多人,因低估這些難題而太快地引入了分散式系統。

最終一致性

我想你應該知道,網站的更新著實需要一點耐心。更新某一個東西之後重新整理螢幕,可是更新的東西還沒有出現。你等了一兩分鐘後,它出現了。

這是一個非常惱人的可用性問題,幾乎可以肯定是由於最終的一致性造成的。你的更新被節點P收到,可是呢,你的請求卻被另一個節點G處理。直到節點G從節點P那兒得到更新之前,你一直處於資料不一致的狀態。最終,它會變成一致的,但在這之前,你會疑惑是不是有什麼東西弄錯了。

像這樣的不一致是令人惱火的,但他們可以更嚴重。業務邏輯會停滯在對不一致的資訊上做出決策,當這種事發生時,在不一致的視窗關閉之前,也難以診斷出到底什麼出了問題。

微服務帶來了最終一致性的問題,是因為他們堅持對去中心的資料管理,這種堅持值得稱讚。單體架構,你可以一次更新一堆東西。微服務則需要多個資源的更新和分散式的處理,這確實頭疼。所以現在,開發者需要意識到一致性問題,在寫任何程式碼之前,弄明白如何檢測資料的不一致。

單體架構在這些問題上同樣不能全身而退。隨著系統的增長,更需要使用快取來提高效能,快取失效是另一個困難的問題。大多數應用程式需要離線鎖,以避免長期的資料庫事務處理。外部系統需要更新,不能與事務管理器協調。商業流程往往更具寬容的不一致性,因為企業對可用性要求更高(業務流程,一直以來對CAP理論都是一種直覺上的理解)。

和其他分散式問題一樣,單體架構也不能完全避免非一致性問題,但它們受到的困擾不多,特別當它們規模很小時。

獨立部署

微服務是DevOps革命後第一新的軟體架構。-- Neal Ford

模組化和分散式系統的複雜性一直伴隨著我整個職業生涯中。但是有一件事情發生了明顯的變化,就是在過去的十年中,有關生產環境的釋出。在二十世紀,生產環境的釋出幾乎總是痛苦且罕見的事情,週末的白天或晚上部署一些能用的軟體。但是現在呢,熟練的團隊頻繁釋出到生產環境,許多團隊實行持續交付,使他們一天能夠在生產環境釋出很多次。

這種轉變已經對軟體產業產生了深遠的影響,並與微服務運動深深交織在一起。當部分的小變化可能導致整個部署失敗,微服務終於被單體架構的部署難題所激發出來。微服務的一個關鍵原則是,每個服務都是系統的一個元件,均可獨立部署。所以現在當你做出改變時,你只需要測試和部署一個小服務。如果你把它搞砸了,你不會把整個系統都搞砸。畢竟,事先對故障進行了設計,即使失敗了,你的元件也不應該停止其他部分的系統工作,儘管功能上有些退化。

這種關係是雙向的。由於許多微服務需要頻繁部署,統一部署行為顯得尤為重要。這就是為什麼應用的快速部署和快速配置是微服務的先決條件。對於任何以此為基礎的服務,你都需要做持續的交付。

持續交付的好處是減少了由想法變成軟體的時間。那麼,團隊可以快速響應市場變化,並快於競爭對手先引入新的功能。

儘管許多人認為持續交付是使用微服務的一個原因,但值得注意的是,即使單體架構也可以持續交付。Facebook和Etsy是兩個最好的例子。還有一些嘗試微服務的例子,因為多個服務需要認真協調才能釋出,因此無法獨立部署[2]。同時我也聽到很多人認為使用微服務能更容易做持續交付,我是不太相信模組化可以使持續釋出更容易,儘管它可以大大提高交付的速度。

運維的複雜性

能夠迅速部署獨立的小單位,是專案開發的一大福音,但由於幾十個應用現在轉變為幾百個小的微服務,給運維額外增加了負擔。許多組織會發現,處理這樣一個迅速變化的工具,有一種令人望而卻步的難度。

這加強了持續交付的重要性。而持續交付是單體架構一項有價值的技能,也是一個微服務所必須的。如果沒有自動化協作,持續交付也無法處理那麼多的服務。運維的複雜性也是由管理這些服務和監控的需求的增加而增加。運維的複雜性對單體架構的應用也有所幫助,不過對微服務來說,這是必然的。

微服務的支持者指出,由於每個服務更小,所以更容易理解。但風險在於,複雜性並沒有消除,它只是轉移到服務之間的相互聯絡中。這會增加運維的複雜性,比如除錯、跨服務的難度。良好的服務邊界的選擇將減少這個問題,但如果用錯地方,則更糟糕。

處理這種運維複雜度,需要一個新的技能和工具——重點是技能。工具仍然是不成熟的,但我的直覺告訴我,即使有更好的工具,微服務還得靠高技能。

然而,對技能和工具的需求不是解決運維複雜性最難的部分。想要高效地解決,你還需要引入一個DevOps文化:開發者和運維之間的緊密合作,每個人都參與軟體交付。文化變革是困難的,尤其是在更大和更老的組織中。如果你不改變這個技能和文化,單體架構的應用只會受到阻礙,而微服務應用則會受到創傷。

技術的多樣性

由於每個微服務都是一個獨立的部署單元,你有相當的自由選擇需要的技術。微服務可以用不同的語言,使用不同的庫,並使用不同的資料儲存方式。這使得團隊可以選擇合適的工具來工作,有些語言和庫更適合某些型別的問題。

技術多樣性通常以最佳的工具為中心進行討論,但往往微服務最大的好處確是更令人頭疼的版本問題。在單體架構中你可以只使用一個單一的版本庫,這種情況經常導致升級出現問題。系統的一部分可能需要升級,來實現使用它的新功能,但不能因為升級而中斷系統的另一部分。處理庫的版本問題是其中的一個難題,因為隨著程式碼庫的增大,難度會呈指數級增長。

這裡有一個風險,有這麼多的技術多樣性,開發團隊會被壓倒。我所認識的大多陣列織都鼓勵有限的一組技術。這種鼓勵是通過提供共同的工具來支援監測,使它更容易將服務穩定在一個小的通用環境中。

不要低估了支援使用新技術的價值。用單體架構系統,早期對語言和框架的決定是很難逆轉的。經過十年左右,這些的決定可能會限制團隊並使團隊陷入尷尬的技術境地。微服務讓團隊嘗試新工具,並逐步一次遷移系統的一個服務,使新老技術有所關聯。

其他因素

在我看來,上面的因素是作為首要權衡指標來考慮的。這裡要討論的一些事情,我認為就沒那麼重要了。

微服務的支持者們經常說服務更容易擴充套件:一個服務得到大量的負載,你就可以擴充套件,而不用對整個應用進行擴充套件。然而,我努力回憶起一個過往的經驗報告,它使我相信,和千篇一律的應用複製相比,它實際上是更有效地進行選擇性擴充套件。

微服務允許你隔離敏感資料以及給資料增加安全性。此外,在保證微服務間互動安全的前提下,微服務難以被攻入。安全問題越來越重要,這可以成為使用微服務的主要考慮因素。即使不用微服務,對於單體架構的系統建立隔離服務來處理敏感資料,也是很常見的。

批評者說微服務應用的測試要比單體架構的應用難度更大。雖然這個真正的困難部分來自於分散式應用程式的複雜性,但還是有好的方法可以測試微服務。在這裡,最重要的是要有紀律,要認真測試,單體架構和微服務應用的測試方法的區別只是第二位的。

總結

任何有關架構的綜述文章都會受到一般性建議的限制。所以讀一篇這樣的文章不能幫助你做決定,但這樣的文章可以幫助你確保考慮到你應該考慮的各種因素。這裡對不同的系統,成本和收益做了不同的權重,甚至成本收益會被顛倒過來(在更復雜的系統中強模組化更好,但對於一個簡單的系統,這就增加了障礙),你所做的任何決定取決於環境中使用條件,評估哪些因素最重要,以及它們如何影響你的特定評估條件。此外,我們對微服務架構經驗相對有限。在一個系統成熟後,你通常只會在這個體系中決定相應的架構。我們還沒有很多關於長期執行的微服務架構的例項。

單體架構和微服務並不是簡單的二選一。兩者都是模糊的定義,意味著大多數系統都將在一個模糊的邊界區域。當然也還有其他系統,不適合這兩個類別的。大多數人,包括我自己,討論微服務時用單體架構作對比,這是因為它們更常見,但我們必須記住,還是有系統不屬於這兩類的。我認為,單體架構和微服務是架構設計領域重要的兩部分。它們值得被討論,因為它們存在有趣的特性,以及有用的討論,但是沒有合適的架構師可以對它們在架構設計領域進行一個全面的區分。

總結起來,微服務的好處:微服務提高了生產效率,但同時也帶來了複雜性。所以如果你可以用單體架構管理好你的系統,那麼就無需微服務。

對微服務的討論不應該讓我們忘記了更重要的問題,驅動軟體專案成功和失敗的重要因素。軟因素如團隊中人的素質,以及他們如何彼此合作,與領域專家的溝通能力,這都會對是否使用微服務有更加的影響。在純技術層面上來講,更重要的是把重點放在乾淨的程式碼、完善的測試,並持續關注架構的演化進步。

腳註

[1] 有些人認為“單體架構“是一種侮辱,總是意味著很糟糕的模組化結構。在微服務世界,大多數人不這樣做,他們將單體架構定義為將整個應用構建為一個獨立的單元。當然,微服務倡導者相信大多數單體架構最終會變成大雜燴,但我不知道有誰會認為建立一個結構良好的單體架構毫無可能。

[2] 能獨立部署服務是微服務定義的一部分。所以這麼說還是有道理的,即必須在其部署時協調服務的架構不是微服務架構。也可以這麼說,很多團隊嘗試微服務架構而陷入麻煩是因為他們最終不得不協調多個服務來部署。

進一步閱讀

  1. Sam Newman在書中第一章給出了微服務帶來的好處列表,這本書對於研究構建微服務系統的細節非常有用。
  2. Benjamin Wootton的文章,微服務-不是免費的午餐!,是最早也是最好關於微服務的缺點的總結文章。

致謝

Brian Mason、Chris Ford、Rebecca Parsons、Rob Miles、Scott Robinson、Stefan Tilkov、Steven Lowe以及Unmesh Joshi和我一起討論這篇文章的草稿。



===================================
譯者介紹
Henry Huang,目前供職於趨勢科技 Trend Micro(南京),負責叢集運維的工作。